The DevMode JSO changes are too complex to integrate as a single change.  Need to find a better strategy.


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@8297 8db76d5a-ed1c-0410-87a9-c151d255dfc7
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 b80057c..1ceeea4 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
@@ -328,7 +328,7 @@
    * @return <code>null</code> if the type is not found
    */
   public JClassType findType(String name) {
-    assert Name.isSourceName(name) : name + " is not a source name";
+    assert Name.isSourceName(name);
     return allTypes.get(name);
   }
 
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 a6ff29d..a789ac0 100644
--- a/dev/core/src/com/google/gwt/dev/shell/CompilingClassLoader.java
+++ b/dev/core/src/com/google/gwt/dev/shell/CompilingClassLoader.java
@@ -19,21 +19,29 @@
 import com.google.gwt.core.client.GwtScriptOnly;
 import com.google.gwt.core.ext.TreeLogger;
 import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.TreeLogger.Type;
+import com.google.gwt.core.ext.typeinfo.JArrayType;
 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.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.HasAnnotation;
 import com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter;
-import com.google.gwt.dev.shell.rewrite.OriginalJsniSignature;
-import com.google.gwt.dev.shell.rewrite.SingleJsoImplSupport;
+import com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter.InstanceMethodOracle;
+import com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter.SingleJsoImplData;
 import com.google.gwt.dev.util.JsniRef;
+import com.google.gwt.dev.util.Name;
 import com.google.gwt.dev.util.Util;
-import com.google.gwt.dev.util.Name.BinaryName;
 import com.google.gwt.dev.util.Name.InternalName;
 import com.google.gwt.dev.util.Name.SourceOrBinaryName;
+import com.google.gwt.dev.util.collect.Lists;
 import com.google.gwt.util.tools.Utility;
 
 import org.apache.commons.collections.map.AbstractReferenceMap;
@@ -56,7 +64,9 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.SortedSet;
 import java.util.Stack;
+import java.util.TreeSet;
 
 /**
  * An isolated {@link ClassLoader} for running all user code. All user files are
@@ -136,6 +146,23 @@
       DispatchClassInfo dispClassInfo = getClassInfoFromClassName(className);
       if (dispClassInfo != null) {
         String memberName = parsed.memberSignature();
+
+        /*
+         * Disallow the use of JSNI references to SingleJsoImpl interface
+         * methods. This policy is due to web-mode dispatch implementation
+         * details; resolving the JSNI reference wouldn't be just be a name
+         * replacement, instead it would be necessary to significantly alter the
+         * semantics of the hand-written JS.
+         */
+        if (singleJsoImplTypes.contains(canonicalizeClassName(className))) {
+          logger.log(TreeLogger.WARN,
+              "Invalid JSNI reference to SingleJsoImpl interface (" + className
+                  + "); consider using a trampoline. "
+                  + "Expect subsequent failures.", new NoSuchFieldError(
+                  jsniMemberRef));
+          return -1;
+        }
+
         int memberId = dispClassInfo.getMemberId(memberName);
         if (memberId < 0) {
           if (!className.startsWith("java.")) {
@@ -267,6 +294,11 @@
         return null;
       }
 
+      // Map JSO type references to the appropriate impl class.
+      if (classRewriter.isJsoIntf(cls.getName())) {
+        cls = getClassFromBinaryName(cls.getName() + "$");
+      }
+
       /*
        * we need to create a new DispatchClassInfo since we have never seen this
        * class before under any source or binary class name
@@ -323,6 +355,325 @@
   }
 
   /**
+   * Implements {@link InstanceMethodOracle} on behalf of the
+   * {@link HostedModeClassRewriter}. Implemented using {@link TypeOracle}.
+   */
+  private class MyInstanceMethodOracle implements InstanceMethodOracle {
+
+    private final Map<String, Set<JClassType>> signatureToDeclaringClasses = new HashMap<String, Set<JClassType>>();
+
+    public MyInstanceMethodOracle(Set<JClassType> jsoTypes,
+        JClassType javaLangObject, SingleJsoImplData jsoData) {
+
+      // Record that the JSO implements its own methods
+      for (JClassType type : jsoTypes) {
+        for (JMethod method : type.getMethods()) {
+          if (!method.isStatic()) {
+            assert !method.isAbstract() : "Abstract method in JSO type "
+                + method;
+            add(type, method);
+          }
+        }
+      }
+
+      /*
+       * Record the implementing types for methods defined in SingleJsoImpl
+       * interfaces. We have to make this pass because of possible variance in
+       * the return types between the abstract method declaration in the
+       * interface and the concrete method.
+       */
+      for (String intfName : jsoData.getSingleJsoIntfTypes()) {
+        // We only store the name in the data block to keep it lightweight
+        JClassType intf = typeOracle.findType(Name.InternalName.toSourceName(intfName));
+        JClassType jso = typeOracle.getSingleJsoImpl(intf);
+        for (JMethod method : intf.getMethods()) {
+          add(jso, method);
+        }
+      }
+
+      // Object clobbers everything.
+      for (JMethod method : javaLangObject.getMethods()) {
+        if (!method.isStatic()) {
+          String signature = createSignature(method);
+          Set<JClassType> declaringClasses = new HashSet<JClassType>();
+          signatureToDeclaringClasses.put(signature, declaringClasses);
+          declaringClasses.add(javaLangObject);
+        }
+      }
+    }
+
+    public String findOriginalDeclaringClass(String desc, String signature) {
+      // Lookup the method.
+      Set<JClassType> declaringClasses = signatureToDeclaringClasses.get(signature);
+      assert declaringClasses != null : "No classes for " + signature;
+      if (declaringClasses.size() == 1) {
+        // Shortcut: if there's only one answer, it must be right.
+        return createDescriptor(declaringClasses.iterator().next());
+      }
+      // Must check for assignability.
+      String sourceName = desc.replace('/', '.');
+      sourceName = sourceName.replace('$', '.');
+      JClassType declaredType = typeOracle.findType(sourceName);
+
+      // Check if I declare this directly.
+      if (declaringClasses.contains(declaredType)) {
+        return desc;
+      }
+
+      // Check to see what type I am assignable to.
+      for (JClassType possibleSupertype : declaringClasses) {
+        if (declaredType.isAssignableTo(possibleSupertype)) {
+          return createDescriptor(possibleSupertype);
+        }
+      }
+      throw new IllegalArgumentException("Could not resolve signature '"
+          + signature + "' from class '" + desc + "'");
+    }
+
+    /**
+     * Record that a given JSO type contains the concrete implementation of a
+     * (possibly abstract) method.
+     */
+    private void add(JClassType type, JMethod method) {
+      String signature = createSignature(method);
+      Set<JClassType> declaringClasses = signatureToDeclaringClasses.get(signature);
+      if (declaringClasses == null) {
+        declaringClasses = new HashSet<JClassType>();
+        signatureToDeclaringClasses.put(signature, declaringClasses);
+      }
+      declaringClasses.add(type);
+    }
+
+    private String createDescriptor(JClassType type) {
+      String jniSignature = type.getJNISignature();
+      return jniSignature.substring(1, jniSignature.length() - 1);
+    }
+
+    private String createSignature(JMethod method) {
+      StringBuffer sb = new StringBuffer(method.getName());
+      sb.append('(');
+      for (JParameter param : method.getParameters()) {
+        sb.append(param.getType().getJNISignature());
+      }
+      sb.append(')');
+      sb.append(method.getReturnType().getJNISignature());
+      String signature = sb.toString();
+      return signature;
+    }
+  }
+
+  /**
+   * Cook up the data we need to support JSO subtypes that implement interfaces
+   * with methods. This includes the set of SingleJsoImpl interfaces actually
+   * implemented by a JSO type, the mangled method names, and the names of the
+   * Methods that should actually implement the virtual functions.
+   * 
+   * Given the current implementation of JSO$ and incremental execution of
+   * rebinds, it's not possible for Generators to produce additional
+   * JavaScriptObject subtypes, so this data can remain static.
+   */
+  private class MySingleJsoImplData implements SingleJsoImplData {
+    private final SortedSet<String> mangledNames = new TreeSet<String>();
+    private final Map<String, List<com.google.gwt.dev.asm.commons.Method>> mangledNamesToDeclarations = new HashMap<String, List<com.google.gwt.dev.asm.commons.Method>>();
+    private final Map<String, List<com.google.gwt.dev.asm.commons.Method>> mangledNamesToImplementations = new HashMap<String, List<com.google.gwt.dev.asm.commons.Method>>();
+    private final SortedSet<String> unmodifiableNames = Collections.unmodifiableSortedSet(mangledNames);
+    private final Set<String> unmodifiableIntfNames = Collections.unmodifiableSet(singleJsoImplTypes);
+
+    public MySingleJsoImplData() {
+      // Loop over all interfaces with JSO implementations
+      typeLoop : for (JClassType type : typeOracle.getSingleJsoImplInterfaces()) {
+        assert type.isInterface() == type : "Expecting interfaces only";
+
+        /*
+         * By preemptively adding all possible mangled names by which a method
+         * could be called, we greatly simplify the logic necessary to rewrite
+         * the call-site.
+         * 
+         * interface A {void m();}
+         * 
+         * interface B extends A {void z();}
+         * 
+         * becomes
+         * 
+         * c_g_p_A_m() -> JsoA$.m$()
+         * 
+         * c_g_p_B_m() -> JsoA$.m$()
+         * 
+         * c_g_p_B_z() -> JsoB$.z$()
+         */
+        for (JMethod intfMethod : type.getOverridableMethods()) {
+          assert intfMethod.isAbstract() : "Expecting only abstract methods";
+
+          /*
+           * It is necessary to locate the implementing type on a per-method
+           * basis. Consider the case of
+           * 
+           * @SingleJsoImpl interface C extends A, B {}
+           * 
+           * Methods inherited from interfaces A and B must be dispatched to
+           * their respective JSO implementations.
+           */
+          JClassType implementingType = typeOracle.getSingleJsoImpl(intfMethod.getEnclosingType());
+
+          if (implementingType == null) {
+            /*
+             * This means that there is no concrete implementation of the
+             * interface by a JSO. Any implementation that might be created by a
+             * Generator won't be a JSO subtype, so we'll just ignore it as an
+             * actionable type. Were Generators ever able to create new JSO
+             * subtypes, we'd have to speculatively rewrite the callsite.
+             */
+            continue typeLoop;
+          }
+
+          /*
+           * Record the type as being actionable.
+           */
+          singleJsoImplTypes.add(canonicalizeClassName(getBinaryName(type)));
+
+          /*
+           * The mangled name adds the current interface like
+           * 
+           * com_foo_Bar_methodName
+           */
+          String mangledName = getBinaryName(type).replace('.', '_') + "_"
+              + intfMethod.getName();
+          mangledNames.add(mangledName);
+
+          /*
+           * Handle virtual overrides by finding the method that we would
+           * normally invoke and using its declaring class as the dispatch
+           * target.
+           */
+          JMethod implementingMethod;
+          while ((implementingMethod = findOverloadUsingErasure(
+              implementingType, intfMethod)) == null) {
+            implementingType = implementingType.getSuperclass();
+          }
+          // implementingmethod and implementingType cannot be null here
+
+          /*
+           * Create a pseudo-method declaration for the interface method. This
+           * should look something like
+           * 
+           * ReturnType method$ (ParamType, ParamType)
+           * 
+           * This must be kept in sync with the WriteJsoImpl class.
+           */
+          {
+            String decl = getBinaryOrPrimitiveName(intfMethod.getReturnType().getErasedType())
+                + " " + intfMethod.getName() + "(";
+            for (JParameter param : intfMethod.getParameters()) {
+              decl += ",";
+              decl += getBinaryOrPrimitiveName(param.getType().getErasedType());
+            }
+            decl += ")";
+
+            com.google.gwt.dev.asm.commons.Method declaration = com.google.gwt.dev.asm.commons.Method.getMethod(decl);
+            addToMap(mangledNamesToDeclarations, mangledName, declaration);
+          }
+
+          /*
+           * Cook up the a pseudo-method declaration for the concrete type. This
+           * should look something like
+           * 
+           * ReturnType method$ (JsoType, ParamType, ParamType)
+           * 
+           * This must be kept in sync with the WriteJsoImpl class.
+           */
+          {
+            String returnName = getBinaryOrPrimitiveName(implementingMethod.getReturnType().getErasedType());
+            String jsoName = getBinaryOrPrimitiveName(implementingType);
+
+            String decl = returnName + " " + intfMethod.getName() + "$ ("
+                + jsoName;
+            for (JParameter param : implementingMethod.getParameters()) {
+              decl += ",";
+              decl += getBinaryOrPrimitiveName(param.getType().getErasedType());
+            }
+            decl += ")";
+
+            com.google.gwt.dev.asm.commons.Method toImplement = com.google.gwt.dev.asm.commons.Method.getMethod(decl);
+            addToMap(mangledNamesToImplementations, mangledName, toImplement);
+          }
+        }
+      }
+
+      if (logger.isLoggable(Type.SPAM)) {
+        TreeLogger dumpLogger = logger.branch(Type.SPAM,
+            "SingleJsoImpl method mappings");
+        for (Map.Entry<String, List<com.google.gwt.dev.asm.commons.Method>> entry : mangledNamesToImplementations.entrySet()) {
+          dumpLogger.log(Type.SPAM, entry.getKey() + " -> " + entry.getValue());
+        }
+      }
+    }
+
+    public List<com.google.gwt.dev.asm.commons.Method> getDeclarations(
+        String mangledName) {
+      List<com.google.gwt.dev.asm.commons.Method> toReturn = mangledNamesToDeclarations.get(mangledName);
+      return toReturn == null ? null : Collections.unmodifiableList(toReturn);
+    }
+
+    public List<com.google.gwt.dev.asm.commons.Method> getImplementations(
+        String mangledName) {
+      List<com.google.gwt.dev.asm.commons.Method> toReturn = mangledNamesToImplementations.get(mangledName);
+      return toReturn == null ? toReturn
+          : Collections.unmodifiableList(toReturn);
+    }
+
+    public SortedSet<String> getMangledNames() {
+      return unmodifiableNames;
+    }
+
+    public Set<String> getSingleJsoIntfTypes() {
+      return unmodifiableIntfNames;
+    }
+
+    /**
+     * Assumes that the usual case is a 1:1 mapping.
+     */
+    private <K, V> void addToMap(Map<K, List<V>> map, K key, V value) {
+      List<V> list = map.get(key);
+      if (list == null) {
+        map.put(key, Lists.create(value));
+      } else {
+        List<V> maybeOther = Lists.add(list, value);
+        if (maybeOther != list) {
+          map.put(key, maybeOther);
+        }
+      }
+    }
+
+    /**
+     * Looks for a concrete implementation of <code>intfMethod</code> in
+     * <code>implementingType</code>.
+     */
+    private JMethod findOverloadUsingErasure(JClassType implementingType,
+        JMethod intfMethod) {
+
+      int numParams = intfMethod.getParameters().length;
+      JType[] erasedTypes = new JType[numParams];
+      for (int i = 0; i < numParams; i++) {
+        erasedTypes[i] = intfMethod.getParameters()[i].getType().getErasedType();
+      }
+
+      outer : for (JMethod method : implementingType.getOverloads(intfMethod.getName())) {
+        JParameter[] params = method.getParameters();
+        if (params.length != numParams) {
+          continue;
+        }
+        for (int i = 0; i < numParams; i++) {
+          if (params[i].getType().getErasedType() != erasedTypes[i]) {
+            continue outer;
+          }
+        }
+        return method;
+      }
+      return null;
+    }
+  }
+
+  /**
    * The names of the bridge classes.
    */
   private static final Map<String, Class<?>> BRIDGE_CLASS_NAMES = new HashMap<String, Class<?>>();
@@ -332,8 +683,7 @@
    * space (thus, they bridge across the spaces).
    */
   private static final Class<?>[] BRIDGE_CLASSES = new Class<?>[] {
-      ShellJavaScriptHost.class, GWTBridge.class, OriginalJsniSignature.class,
-      SingleJsoImplSupport.class};
+      ShellJavaScriptHost.class, GWTBridge.class};
 
   private static final boolean CLASS_DUMP = Boolean.getBoolean("gwt.dev.classDump");
 
@@ -480,6 +830,8 @@
 
   private ShellJavaScriptHost shellJavaScriptHost;
 
+  private final Set<String> singleJsoImplTypes = new HashSet<String>();
+
   /**
    * Used by {@link #findClass(String)} to prevent reentrant JSNI injection.
    */
@@ -509,10 +861,36 @@
 
     ensureJavaScriptHostBytes(logger);
 
-    // Create a class rewriter based on availability of JSO class.
+    // Create a class rewriter based on all the subtypes of the JSO class.
     JClassType jsoType = typeOracle.findType(JsValueGlue.JSO_CLASS);
     if (jsoType != null) {
-      classRewriter = new HostedModeClassRewriter(typeOracle);
+
+      // Create a set of binary names.
+      Set<JClassType> jsoTypes = new HashSet<JClassType>();
+      JClassType[] jsoSubtypes = jsoType.getSubtypes();
+      Collections.addAll(jsoTypes, jsoSubtypes);
+      jsoTypes.add(jsoType);
+
+      Set<String> jsoTypeNames = new HashSet<String>();
+      Map<String, List<String>> jsoSuperTypes = new HashMap<String, List<String>>();
+      for (JClassType type : jsoTypes) {
+        List<String> types = new ArrayList<String>();
+        types.add(getBinaryName(type.getSuperclass()));
+        for (JClassType impl : type.getImplementedInterfaces()) {
+          types.add(getBinaryName(impl));
+        }
+
+        String binaryName = getBinaryName(type);
+        jsoTypeNames.add(binaryName);
+        jsoSuperTypes.put(binaryName, types);
+      }
+
+      SingleJsoImplData singleJsoImplData = new MySingleJsoImplData();
+
+      MyInstanceMethodOracle mapper = new MyInstanceMethodOracle(jsoTypes,
+          typeOracle.getJavaLangObject(), singleJsoImplData);
+      classRewriter = new HostedModeClassRewriter(jsoTypeNames, jsoSuperTypes,
+          singleJsoImplData, mapper);
     } else {
       // If we couldn't find the JSO class, we don't need to do any rewrites.
       classRewriter = null;
@@ -642,8 +1020,8 @@
      * when loading a JSO interface class; just wait until the implementation
      * class is loaded.
      */
-    {
-      CompilationUnit unit = getUnitForClassName(BinaryName.toInternalName(className));
+    if (!classRewriter.isJsoIntf(className)) {
+      CompilationUnit unit = getUnitForClassName(canonicalizeClassName(className));
       if (unit != null) {
         toInject.push(unit);
       }
@@ -686,27 +1064,37 @@
     dispClassInfoOracle.clear();
   }
 
+  /**
+   * Convert a binary class name into a resource-like name.
+   */
+  private String canonicalizeClassName(String className) {
+    String lookupClassName = className.replace('.', '/');
+    // A JSO impl class ends with $, strip it
+    if (classRewriter != null && classRewriter.isJsoImpl(className)) {
+      lookupClassName = lookupClassName.substring(0,
+          lookupClassName.length() - 1);
+    }
+    return lookupClassName;
+  }
+
   @SuppressWarnings("deprecation")
   private byte[] findClassBytes(String className) {
     if (JavaScriptHost.class.getName().equals(className)) {
       // No need to rewrite.
       return javaScriptHostBytes;
-    } else if (className.endsWith(HostedModeClassRewriter.SINGLE_JSO_IMPL_ADJUNCT_SUFFIX)) {
-      byte[] bytes = classRewriter.writeSingleJsoImplAdjunct(className);
+    }
+
+    if (classRewriter != null && classRewriter.isJsoIntf(className)) {
+      // Generate a synthetic JSO interface class.
+      byte[] newBytes = classRewriter.writeJsoIntf(className);
       if (CLASS_DUMP) {
-        classDump(className, bytes);
+        classDump(className, newBytes);
       }
-      return bytes;
-    } else if (className.startsWith(HostedModeClassRewriter.DISAMBIGUATOR_TYPE_NAME)) {
-      byte[] bytes = classRewriter.writeConstructorDisambiguationType(className);
-      if (CLASS_DUMP) {
-        classDump(className, bytes);
-      }
-      return bytes;
+      return newBytes;
     }
 
     // A JSO impl class needs the class bytes for the original class.
-    String lookupClassName = BinaryName.toInternalName(className);
+    String lookupClassName = canonicalizeClassName(className);
 
     CompiledClass compiledClass = compilationState.getClassFileMap().get(
         lookupClassName);
@@ -767,8 +1155,8 @@
       if (unit != null) {
         anonymousClassMap = unit.getAnonymousClassMap();
       }
-      byte[] newBytes = classRewriter.rewrite(className, classBytes,
-          anonymousClassMap);
+      byte[] newBytes = classRewriter.rewrite(typeOracle, className,
+          classBytes, anonymousClassMap);
       if (CLASS_DUMP) {
         if (!Arrays.equals(classBytes, newBytes)) {
           classDump(className, newBytes);
@@ -779,6 +1167,29 @@
     return classBytes;
   }
 
+  private String getBinaryName(JClassType type) {
+    String name = type.getPackage().getName() + '.';
+    name += type.getName().replace('.', '$');
+    return name;
+  }
+
+  private String getBinaryOrPrimitiveName(JType type) {
+    JArrayType asArray = type.isArray();
+    JClassType asClass = type.isClassOrInterface();
+    JPrimitiveType asPrimitive = type.isPrimitive();
+    if (asClass != null) {
+      return getBinaryName(asClass);
+    } else if (asPrimitive != null) {
+      return asPrimitive.getQualifiedSourceName();
+    } else if (asArray != null) {
+      JType componentType = asArray.getComponentType();
+      return getBinaryOrPrimitiveName(componentType) + "[]";
+    } 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.
@@ -801,8 +1212,6 @@
     if (unit == null || unit.getJsniMethods() == null) {
       return;
     }
-    logger.log(TreeLogger.SPAM, "Injecting JSNI methods for "
-        + unit.getTypeName());
     shellJavaScriptHost.createNativeMethods(logger, unit.getJsniMethods(), this);
   }
 
diff --git a/dev/core/src/com/google/gwt/dev/shell/DispatchClassInfo.java b/dev/core/src/com/google/gwt/dev/shell/DispatchClassInfo.java
index 9fecfa2..27819b3 100644
--- a/dev/core/src/com/google/gwt/dev/shell/DispatchClassInfo.java
+++ b/dev/core/src/com/google/gwt/dev/shell/DispatchClassInfo.java
@@ -15,10 +15,8 @@
  */
 package com.google.gwt.dev.shell;
 
-import com.google.gwt.dev.shell.rewrite.OriginalJsniSignature;
 import com.google.gwt.dev.util.JsniRef;
 
-import java.lang.reflect.AccessibleObject;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
 import java.lang.reflect.Member;
@@ -155,21 +153,6 @@
     String name;
     Class<?>[] paramTypes;
 
-    if (member instanceof AccessibleObject) {
-      AccessibleObject accessibleObject = (AccessibleObject) member;
-      OriginalJsniSignature signature = accessibleObject.getAnnotation(OriginalJsniSignature.class);
-      if (signature != null) {
-        StringBuilder sb = new StringBuilder();
-        sb.append(signature.name());
-        if (wildcardParamList) {
-          sb.append("(*)");
-        } else {
-          sb.append(signature.paramList());
-        }
-        return sb.toString();
-      }
-    }
-
     if (member instanceof Field) {
       return member.getName();
     } else if (member instanceof SyntheticClassMember) {
@@ -185,7 +168,7 @@
           + member.getClass().getName());
     }
 
-    StringBuilder sb = new StringBuilder();
+    StringBuffer sb = new StringBuffer();
     sb.append(name);
     sb.append("(");
     if (wildcardParamList) {
diff --git a/dev/core/src/com/google/gwt/dev/shell/JsValueGlue.java b/dev/core/src/com/google/gwt/dev/shell/JsValueGlue.java
index fdefaed..02e2cdd 100644
--- a/dev/core/src/com/google/gwt/dev/shell/JsValueGlue.java
+++ b/dev/core/src/com/google/gwt/dev/shell/JsValueGlue.java
@@ -16,7 +16,6 @@
 package com.google.gwt.dev.shell;
 
 import com.google.gwt.core.ext.TreeLogger;
-import com.google.gwt.dev.shell.rewrite.SingleJsoImplSupport;
 import com.google.gwt.dev.util.TypeInfo;
 
 import java.lang.reflect.Constructor;
@@ -30,13 +29,11 @@
 public final class JsValueGlue {
   public static final String HOSTED_MODE_REFERENCE = "hostedModeReference";
   public static final String JSO_CLASS = "com.google.gwt.core.client.JavaScriptObject";
+  public static final String JSO_IMPL_CLASS = "com.google.gwt.core.client.JavaScriptObject$";
 
   /**
-   * Create a JavaScriptObject instance referring to this JavaScript object,
-   * optionally wrapping the object in the desired JSO facade type.
+   * Create a JavaScriptObject instance referring to this JavaScript object.
    * 
-   * @param desiredType the subclass of JavaScriptObject that the calling code
-   *          wishes to see, or <code>null</code>
    * @param classLoader the classLoader to create from
    * @return the constructed JavaScriptObject
    */
@@ -44,18 +41,23 @@
       CompilingClassLoader classLoader) {
     Throwable caught;
     try {
-      Class<?> jsoType = Class.forName(JSO_CLASS, true, classLoader);
-
       // See if there's already a wrapper object (assures identity comparison).
       Object jso = classLoader.getCachedJso(value.getJavaScriptObjectPointer());
-      if (jso == null) {
-        // Instantiate the JSO instance.
-        Constructor<?> ctor = jsoType.getDeclaredConstructor(Object.class);
-        jso = ctor.newInstance(value);
-
-        classLoader.putCachedJso(value.getJavaScriptObjectPointer(), jso);
+      if (jso != null) {
+        return jso;
       }
 
+      // Instantiate the JSO class.
+      Class<?> jsoType = Class.forName(JSO_IMPL_CLASS, true, classLoader);
+      Constructor<?> ctor = jsoType.getDeclaredConstructor();
+      ctor.setAccessible(true);
+      jso = ctor.newInstance();
+
+      // Set the reference field to this JsValue using reflection.
+      Field referenceField = jsoType.getField(HOSTED_MODE_REFERENCE);
+      referenceField.set(jso, value);
+
+      classLoader.putCachedJso(value.getJavaScriptObjectPointer(), jso);
       return jso;
     } catch (InstantiationException e) {
       caught = e;
@@ -71,6 +73,8 @@
       caught = e;
     } catch (ClassNotFoundException e) {
       caught = e;
+    } catch (NoSuchFieldException e) {
+      caught = e;
     }
     throw new RuntimeException("Error creating JavaScript object", caught);
   }
@@ -84,7 +88,7 @@
    * @param msgPrefix a prefix for error/warning messages
    * @return the object reference
    * @throws com.google.gwt.dev.shell.HostedModeException if the JavaScript
-   *           object is not assignable to the supplied type.
+   *     object is not assignable to the supplied type.
    */
   @SuppressWarnings("unchecked")
   public static <T> T get(JsValue value, CompilingClassLoader cl,
@@ -162,11 +166,7 @@
       return type.cast(value.getString());
     }
     if (value.isJavaScriptObject()) {
-      Object jso = createJavaScriptObject(value, cl);
-      if (type != Object.class) {
-        jso = SingleJsoImplSupport.cast(jso, type);
-      }
-      return type.cast(jso);
+      return type.cast(createJavaScriptObject(value, cl));
     }
 
     // Just don't know what do to with this.
@@ -217,8 +217,8 @@
     } else {
       // not a boxed primitive
       try {
-        Class<?> jsoType = Class.forName(JSO_CLASS, false, cl);
-        if (jsoType.isInstance(obj)) {
+        Class<?> jsoType = Class.forName(JSO_IMPL_CLASS, false, cl);
+        if (jsoType == obj.getClass()) {
           JsValue jsObject = getUnderlyingObject(obj);
           value.setValue(jsObject);
           return;
@@ -258,10 +258,9 @@
       }
       intVal = (int) doubleVal;
       if (intVal != doubleVal) {
-        ModuleSpace.getLogger().log(
-            TreeLogger.WARN,
+        ModuleSpace.getLogger().log(TreeLogger.WARN,
             msgPrefix + ": Rounding double (" + doubleVal + ") to int for "
-                + typeName, null);
+            + typeName, null);
       }
     } else {
       throw new HostedModeException(msgPrefix + ": JS value of type "
diff --git a/dev/core/src/com/google/gwt/dev/shell/rewrite/DebugAnalyzerAdapter.java b/dev/core/src/com/google/gwt/dev/shell/rewrite/DebugAnalyzerAdapter.java
deleted file mode 100644
index 44bbf8a..0000000
--- a/dev/core/src/com/google/gwt/dev/shell/rewrite/DebugAnalyzerAdapter.java
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Copyright 2010 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.dev.shell.rewrite;
-
-import static com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter.EXTRA_DEBUG_DATA;
-
-import com.google.gwt.dev.asm.Attribute;
-import com.google.gwt.dev.asm.ByteVector;
-import com.google.gwt.dev.asm.ClassWriter;
-import com.google.gwt.dev.asm.Label;
-import com.google.gwt.dev.asm.MethodVisitor;
-import com.google.gwt.dev.asm.commons.AnalyzerAdapter;
-
-import java.io.BufferedReader;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.util.LinkedHashMap;
-import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * A utility base class that allows rewriting passes to record label-based
- * messages in the emitted bytecode.
- */
-class DebugAnalyzerAdapter extends AnalyzerAdapter {
-  /**
-   * Used to record debugging data generated by {@link #recordDebugData}.
-   * 
-   * @see #visitEnd()
-   */
-  private static class DebugAttribute extends Attribute {
-    private final LinkedHashMap<Label, String> debugData;
-
-    private DebugAttribute(String name, LinkedHashMap<Label, String> debugData) {
-      super(name);
-      this.debugData = debugData;
-    }
-
-    @Override
-    protected Label[] getLabels() {
-      return debugData.keySet().toArray(new Label[debugData.size()]);
-    }
-
-    @Override
-    protected ByteVector write(ClassWriter cw, byte[] code, int len,
-        int maxStack, int maxLocals) {
-      StringBuilder sb = new StringBuilder();
-      for (Map.Entry<Label, String> entry : debugData.entrySet()) {
-        String line = "Offset " + entry.getKey().getOffset() + " : "
-            + entry.getValue() + "\n";
-        sb.append(line);
-      }
-      ByteVector toReturn = new ByteVector();
-      toReturn.putUTF8(sb.toString());
-      return toReturn;
-    }
-  }
-
-  /**
-   * Decodes the UTF8-as-octets output from javap.
-   */
-  public static void main(String args[]) throws IOException {
-    BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
-    ByteArrayOutputStream bytes = new ByteArrayOutputStream();
-    Pattern p = Pattern.compile("[0-9A-F]{2}");
-    String line;
-    while ((line = reader.readLine()) != null) {
-      if (line.length() > 0) {
-        Matcher m = p.matcher(line);
-        while (m.find()) {
-          bytes.write(Byte.parseByte(m.group(), 16));
-        }
-      } else {
-        String message = new String(bytes.toByteArray());
-        System.out.println(message);
-        bytes = new ByteArrayOutputStream();
-      }
-    }
-  }
-
-  private final LinkedHashMap<Label, String> debugData = EXTRA_DEBUG_DATA
-      ? new LinkedHashMap<Label, String>() : null;
-
-  protected DebugAnalyzerAdapter(String owner, int access, String name,
-      String desc, MethodVisitor mv) {
-    super(owner, access, name, desc, mv);
-  }
-
-  /**
-   * Record debugging data, if any, in an extra attribute on the method.
-   */
-  @Override
-  public void visitEnd() {
-    if (EXTRA_DEBUG_DATA && !debugData.isEmpty()) {
-      super.visitAttribute(new DebugAttribute(getClass().getCanonicalName(),
-          debugData));
-    }
-    super.visitEnd();
-  }
-
-  /**
-   * When things go wrong in the field, users can enable rewritten class-file
-   * dumping and send us the class files. Most of the other rewriting passes add
-   * whole classes or methods so their effects are easy to find. Since this
-   * rewriting pass is rather subtle, we'll add an extra method attribute that
-   * contains a UTF8-encoded diagnostic log.
-   */
-  protected void recordDebugData(String message) {
-    if (EXTRA_DEBUG_DATA) {
-      Label castLocation = new Label();
-      debugData.put(castLocation, message);
-      super.visitLabel(castLocation);
-    }
-  }
-}
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 ca1f488..319aeb3 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,26 +15,22 @@
  */
 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.ClassReader;
 import com.google.gwt.dev.asm.ClassVisitor;
 import com.google.gwt.dev.asm.ClassWriter;
 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.shell.JsValueGlue;
-import com.google.gwt.dev.util.Name.BinaryName;
-import com.google.gwt.dev.util.Name.InternalName;
-import com.google.gwt.dev.util.collect.Lists;
 
-import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.LinkedList;
 import java.util.List;
+import java.util.ListIterator;
 import java.util.Map;
 import java.util.Set;
+import java.util.SortedSet;
 
 /**
  * This class performs any and all byte code rewriting needed to make hosted
@@ -42,168 +38,25 @@
  * <ol>
  * <li>Rewrites all native methods into non-native thunks to call JSNI via
  * {@link com.google.gwt.dev.shell.JavaScriptHost}.</li>
- * <li>JavaScriptObject and its subtypes gain a static {@value #REWRAP_METHOD}
- * method and new constructor methods.</li>
- * <li>All uses of <code>==</code> object comparisons gain an explicit cast to
- * Object to force canonicalization.</li>
- * <li>Casts and assignments to Object, JavaScriptObject, and interface types
- * have runtime type-fitting code added to support the not-really-a-type
- * semantic.</li>
- * <li>All interfaces that a JSO may implement have an adjunct class generated
- * that aids the SingleJsoImpl cast operations.</li>
+ * <li>Rewrites all JSO types into an interface type (which retains the original
+ * name) and an implementation type (which has a $ appended).</li>
+ * <li>All JSO interface types are empty and mirror the original type hierarchy.
+ * </li>
+ * <li>All JSO impl types contain the guts of the original type, except that all
+ * instance methods are reimplemented as statics.</li>
+ * <li>Calls sites to JSO types rewritten to dispatch to impl types. Any virtual
+ * calls are also made static. Static field references to JSO types reference
+ * static fields in the the impl class.</li>
+ * <li>JavaScriptObject$ implements all the interface types and is the only
+ * instantiable type.</li>
  * </ol>
  * 
  * @see RewriteJsniMethods
+ * @see RewriteRefsToJsoClasses
+ * @see WriteJsoInterface
  * @see WriteJsoImpl
- * @see RewriteObjectComparisons
- * @see RewriteJsoCasts
- * @see WriteSingleJsoSupportCode
  */
 public class HostedModeClassRewriter {
-
-  /**
-   * An oracle to answer queries from the rewriting passes about the
-   * type-system.
-   */
-  static class RewriterOracle {
-    /**
-     * Used by {@link #getConstructorDisambiguator}.
-     */
-    private static final String[] EMPTY_STRING = new String[0];
-
-    private final Map<String, String[]> arrayDisambiguations = new HashMap<String, String[]>();
-
-    private final JClassType jsoType;
-
-    /**
-     * The TypeOracle for the module being rewritten. We need the typeOracle to
-     * provide a live view of the module's type-system, since Generators may add
-     * new JSO types.
-     */
-    private final TypeOracle typeOracle;
-
-    public RewriterOracle(TypeOracle typeOracle) {
-      this.typeOracle = typeOracle;
-      jsoType = typeOracle.findType(JsValueGlue.JSO_CLASS);
-      assert jsoType != null : "No JavaScriptObject type";
-    }
-
-    /**
-     * Returns <code>true</code> if a given type is potentially assignable from
-     * a JSO subtype.
-     */
-    public boolean couldContainJso(String internalName) {
-      return "java/lang/Object".equals(internalName)
-          || JAVASCRIPTOBJECT_DESC.equals(internalName)
-          || isJsoOrSubtype(internalName)
-          || (!internalName.startsWith("java/") && isInterface(internalName));
-    }
-
-    public String[] getAllSuperInterfaces(String[] interfaces) {
-      Set<String> toReturn = new HashSet<String>();
-      Set<JClassType> seen = new HashSet<JClassType>();
-      List<JClassType> queue = new LinkedList<JClassType>();
-
-      for (String intf : interfaces) {
-        JClassType type = typeOracle.findType(InternalName.toSourceName(intf));
-        if (type == null) {
-          throw new RuntimeException("Unknown type " + intf);
-        }
-        queue.add(type);
-      }
-
-      while (!queue.isEmpty()) {
-        JClassType intf = queue.remove(0);
-        if (seen.contains(intf)) {
-          continue;
-        }
-        seen.add(intf);
-        toReturn.add(BinaryName.toInternalName(intf.getQualifiedBinaryName()));
-        queue.addAll(Arrays.asList(intf.getImplementedInterfaces()));
-      }
-
-      String[] array = toReturn.toArray(new String[toReturn.size()]);
-      Arrays.sort(array);
-      return array;
-    }
-
-    /**
-     * In order to be able to upcast all JSO subtype arrays to JavaScriptObject
-     * arrays, it is necessary to be able to maintain distinguishing method
-     * descriptors. If the given descriptor will be affected by the array upcast
-     * transformation, this method will return an array containing the internal
-     * names of any additional types that should be appended to the descriptor.
-     */
-    public String[] getArrayDisambiguator(String desc) {
-      String[] cached = arrayDisambiguations.get(desc);
-      if (cached != null) {
-        return cached;
-      }
-
-      List<String> toReturn = Lists.create();
-      for (Type t : Type.getArgumentTypes(desc)) {
-        if (t.getSort() == Type.ARRAY
-            && t.getElementType().getSort() == Type.OBJECT) {
-          String leafName = t.getElementType().getInternalName();
-          if (isJsoOrSubtype(leafName)) {
-            String disambiguatotr = DISAMBIGUATOR_TYPE_INTERNAL_NAME + "$"
-                + InternalName.toIdentifier(leafName);
-            toReturn = Lists.add(toReturn, disambiguatotr);
-          }
-        }
-      }
-
-      String[] array;
-      if (toReturn.isEmpty()) {
-        array = EMPTY_STRING;
-      } else {
-        array = toReturn.toArray(new String[toReturn.size()]);
-      }
-      arrayDisambiguations.put(desc, array);
-
-      return array;
-    }
-
-    public boolean isInterface(String internalName) {
-      String sourceName = InternalName.toSourceName(internalName);
-      if (sourceName.contains("$")) {
-        // Anonymous type
-        return false;
-      }
-      JClassType type = typeOracle.findType(sourceName);
-      return type != null && type.isInterface() != null;
-    }
-
-    public boolean isJsoOrSubtype(String internalName) {
-      String sourceName = InternalName.toSourceName(internalName);
-      if (sourceName.contains("$")) {
-        // Anonymous type
-        return false;
-      }
-      JClassType type = typeOracle.findType(sourceName);
-      return type != null && jsoType.isAssignableFrom(type);
-    }
-
-    public boolean isTagInterface(String internalName) {
-      String sourceName = InternalName.toSourceName(internalName);
-      if (sourceName.contains("$")) {
-        // Anonymous type
-        return false;
-      }
-      JClassType type = typeOracle.findType(sourceName);
-      return type != null && type.isInterface() != null
-          && type.getOverridableMethods().length == 0;
-    }
-
-    /**
-     * Returns <code>true</code> if a JSO assigned to a slot/field of this type
-     * requires canonicalizing the JSO wrapper.
-     */
-    public boolean jsoAssignmentRequiresCanonicalization(String internalName) {
-      return "java/lang/Object".equals(internalName);
-    }
-  }
-
   /*
    * Note: this rewriter operates on a class-by-class basis and has no global
    * view on the entire system. However, its operation requires certain global
@@ -212,68 +65,160 @@
    */
 
   /**
-   * Used in CompilingClassLoader to trigger a call to
-   * {@link #writeConstructorDisambiguationType}.
+   * Maps instance methods to the class in which they are declared. This must be
+   * provided by the caller since it requires global program state.
    */
-  public static final String DISAMBIGUATOR_TYPE_NAME = "com.google.gwt.dev.shell.rewrite.$Disambiguator";
+  public interface InstanceMethodOracle {
+
+    /**
+     * For a given instance method and declared enclosing class (which must be a
+     * JSO subtype), find the class in which that method was originally
+     * declared. Methods declared on Object will return "java/lang/Object".
+     * Static methods will always return <code>declaredClass</code>.
+     * 
+     * @param declaredClass a descriptor of the static type of the qualifier
+     * @param signature the binary signature of the method
+     * @return the descriptor of the class in which that method was declared,
+     *         which will either be <code>declaredClass</code> or some
+     *         superclass
+     * @throws IllegalArgumentException if the method could not be found
+     */
+    String findOriginalDeclaringClass(String declaredClass, String signature);
+  }
 
   /**
-   * Used in CompilingClassLoader to trigger a call to
-   * {@link #writeSingleJsoImplAdjunct}.
+   * Contains data about how SingleJsoImpl methods are to be dispatched.
    */
-  public static final String SINGLE_JSO_IMPL_ADJUNCT_SUFFIX = "$$singleJsoImpl";
+  public interface SingleJsoImplData {
+    /**
+     * Returns the method declarations that should be generated for a given
+     * mangled method name. {@link #getDeclarations} and
+     * {@link #getImplementations} maintain a pairwise mapping.
+     */
+    List<Method> getDeclarations(String mangledName);
 
-  static final String CANONICAL_FIELD = "canonicalJso";
+    /**
+     * Returns the implementations that the method declarations above should
+     * delegate to.{@link #getDeclarations} and {@link #getImplementations}
+     * maintain a pairwise mapping.
+     */
+    List<Method> getImplementations(String mangledName);
 
-  static final String DISAMBIGUATOR_TYPE_INTERNAL_NAME = BinaryName.toInternalName(DISAMBIGUATOR_TYPE_NAME);
+    /**
+     * Returns all of the mangled method names for SingleJsoImpl methods.
+     */
+    SortedSet<String> getMangledNames();
 
-  static final boolean EXTRA_DEBUG_DATA = Boolean.getBoolean("gwt.dev.classDump");
+    /**
+     * Returns the internal names of all interface types implemented by JSOs.
+     */
+    Set<String> getSingleJsoIntfTypes();
+  }
 
-  static final String JAVASCRIPTOBJECT_DESC = BinaryName.toInternalName(JsValueGlue.JSO_CLASS);
+  static final String JAVASCRIPTOBJECT_DESC = JsValueGlue.JSO_CLASS.replace(
+      '.', '/');
+
+  static final String JAVASCRIPTOBJECT_IMPL_DESC = JsValueGlue.JSO_IMPL_CLASS.replace(
+      '.', '/');
 
   static final String REFERENCE_FIELD = JsValueGlue.HOSTED_MODE_REFERENCE;
 
-  static final String REWRAP_METHOD = "$rewrap";
+  static String addSyntheticThisParam(String owner, String methodDescriptor) {
+    return "(L" + owner + ";" + methodDescriptor.substring(1);
+  }
 
-  static final String SINGLE_JSO_IMPL_FIELD = "$singleJsoImpl";
-
-  static final String SINGLE_JSO_IMPL_CAST_METHOD = "cast$";
-
-  static final String SINGLE_JSO_IMPL_CAST_TO_OBJECT_METHOD = "castToObject$";
-
-  static final String SINGLE_JSO_IMPL_INSTANCEOF_METHOD = "instanceOf$";
-
-  static final String SINGLE_JSO_IMPL_SUPPORT_CLASS = Type.getInternalName(SingleJsoImplSupport.class);
-
-  static final int SYSTEM_CLASS_VERSION = Double.parseDouble(System.getProperty("java.class.version")) < Opcodes.V1_6
-      ? Opcodes.V1_5 : Opcodes.V1_6;
+  private static String toDescriptor(String jsoSubtype) {
+    return jsoSubtype.replace('.', '/');
+  }
 
   /**
-   * Passed into the rewriting visitors.
+   * An unmodifiable set of descriptors containing the implementation form of
+   * <code>JavaScriptObject</code> and all subclasses.
    */
-  private final RewriterOracle rewriterOracle;
+  private final Set<String> jsoImplDescs;
 
   /**
-   * Creates a new {@link HostedModeClassRewriter}.
+   * An unmodifiable set of descriptors containing the interface form of
+   * <code>JavaScriptObject</code> and all subclasses.
+   */
+  private final Set<String> jsoIntfDescs;
+
+  private final SingleJsoImplData jsoData;
+
+  /**
+   * Records the superclass of every JSO for generating empty JSO interfaces.
+   */
+  private final Map<String, List<String>> jsoSuperDescs;
+
+  /**
+   * Maps methods to the class in which they are declared.
+   */
+  private InstanceMethodOracle mapper;
+
+  /**
+   * Creates a new {@link HostedModeClassRewriter} for a specified set of
+   * subclasses of JavaScriptObject.
    * 
-   * @param typeOracle The TypeOracle for the GWT module that is being rewritten
+   * @param jsoSubtypes a set of binary type names representing JavaScriptObject
+   *          and all of its subtypes of
+   * @param mapper maps methods to the class in which they are declared
    */
-  public HostedModeClassRewriter(TypeOracle typeOracle) {
-    rewriterOracle = new RewriterOracle(typeOracle);
+  public HostedModeClassRewriter(Set<String> jsoSubtypes,
+      Map<String, List<String>> jsoSuperTypes, SingleJsoImplData jsoData,
+      InstanceMethodOracle mapper) {
+    Set<String> buildJsoIntfDescs = new HashSet<String>();
+    Set<String> buildJsoImplDescs = new HashSet<String>();
+    Map<String, List<String>> buildJsoSuperDescs = new HashMap<String, List<String>>();
+    for (String jsoSubtype : jsoSubtypes) {
+      String desc = toDescriptor(jsoSubtype);
+      buildJsoIntfDescs.add(desc);
+      buildJsoImplDescs.add(desc + "$");
+
+      List<String> superTypes = jsoSuperTypes.get(jsoSubtype);
+      assert (superTypes != null);
+      assert (superTypes.size() > 0);
+      for (ListIterator<String> i = superTypes.listIterator(); i.hasNext();) {
+        i.set(toDescriptor(i.next()));
+      }
+      buildJsoSuperDescs.put(desc, Collections.unmodifiableList(superTypes));
+    }
+
+    this.jsoIntfDescs = Collections.unmodifiableSet(buildJsoIntfDescs);
+    this.jsoImplDescs = Collections.unmodifiableSet(buildJsoImplDescs);
+    this.jsoSuperDescs = Collections.unmodifiableMap(buildJsoSuperDescs);
+    this.jsoData = jsoData;
+    this.mapper = mapper;
+  }
+
+  /**
+   * Returns <code>true</code> if the class is the implementation class for a
+   * JSO subtype.
+   */
+  public boolean isJsoImpl(String className) {
+    return jsoImplDescs.contains(toDescriptor(className));
+  }
+
+  /**
+   * Returns <code>true</code> if the class is the interface class for a JSO
+   * subtype.
+   */
+  public boolean isJsoIntf(String className) {
+    return jsoIntfDescs.contains(toDescriptor(className));
   }
 
   /**
    * Performs rewriting transformations on a class.
    * 
+   * @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(String className, byte[] classBytes,
-      Map<String, String> anonymousClassMap) {
-    classBytes = maybeUpgradeBytecode(classBytes);
-    String desc = BinaryName.toInternalName(className);
+  public byte[] rewrite(TypeOracle typeOracle, String className,
+      byte[] classBytes, Map<String, String> anonymousClassMap) {
+    String desc = toDescriptor(className);
+    assert (!jsoIntfDescs.contains(desc));
 
     // The ASM model is to chain a bunch of visitors together.
     ClassWriter writer = new ClassWriter(0);
@@ -282,75 +227,49 @@
     // v = new CheckClassAdapter(v);
     // v = new TraceClassVisitor(v, new PrintWriter(System.out));
 
-    v = new WriteSingleJsoSupportCode(v, rewriterOracle);
+    v = new RewriteSingleJsoImplDispatches(v, typeOracle, jsoData);
 
-    v = new RewriteJsoCasts(v, rewriterOracle);
+    v = new RewriteRefsToJsoClasses(v, jsoIntfDescs, mapper);
 
-    v = new RewriteJsoArrays(v, rewriterOracle);
-
-    v = new RewriteObjectComparisons(v, rewriterOracle);
-
-    if (rewriterOracle.isJsoOrSubtype(desc)) {
-      v = WriteJsoImpl.create(v, desc);
+    if (jsoImplDescs.contains(desc)) {
+      v = WriteJsoImpl.create(v, desc, jsoIntfDescs, mapper, jsoData);
     }
 
     v = new RewriteJsniMethods(v, anonymousClassMap);
 
-    if (SYSTEM_CLASS_VERSION < Opcodes.V1_6) {
+    if (Double.parseDouble(System.getProperty("java.class.version")) < Opcodes.V1_6) {
       v = new ForceClassVersion15(v);
     }
 
-    // We need EXPAND_FRAMES here for RewriteJsoCasts
-    new ClassReader(classBytes).accept(v, ClassReader.EXPAND_FRAMES);
+    new ClassReader(classBytes).accept(v, 0);
     return writer.toByteArray();
   }
 
-  /**
-   * Synthesize a class file that is used to disambiguate constructors that have
-   * JSO subtype array parameters that have been upcast to JavaScriptObject
-   * arrays.
-   */
-  public byte[] writeConstructorDisambiguationType(String className) {
-    // Keep the synthetic class creation code next to where its consumed
-    return RewriteJsoArrays.writeConstructorDisambiguationType(className);
-  }
+  public byte[] writeJsoIntf(String className) {
+    String desc = toDescriptor(className);
+    assert (jsoIntfDescs.contains(desc));
+    assert (jsoSuperDescs.containsKey(desc));
+    List<String> superDescs = jsoSuperDescs.get(desc);
+    assert (superDescs != null);
+    assert (superDescs.size() > 0);
 
-  /**
-   * Synthesize a class file that implements an interface's SingleJsoImpl
-   * adjunct type.
-   */
-  public byte[] writeSingleJsoImplAdjunct(String className) {
-    // Keep the synthetic class creation code next to where its consumed
-    return WriteSingleJsoSupportCode.writeSingleJsoImplAdjunct(className);
-  }
+    // The ASM model is to chain a bunch of visitors together.
+    ClassWriter writer = new ClassWriter(0);
+    ClassVisitor v = writer;
 
-  /**
-   * AnalyzerAdapter, and thus RewriteJsoCasts, requires StackFrameMap data
-   * which is new in version 50 (Java 1.6) bytecode. This method will only do
-   * work if the input bytecode is less than version 50. This would occur when
-   * running on Java 1.5 or if Emma support is enabled (since Emma is from
-   * 2005).
-   */
-  private byte[] maybeUpgradeBytecode(byte[] classBytes) {
-    // Major version is stored at offset 7 in the class file format
-    if (classBytes[7] < Opcodes.V1_6) {
-      // Get ASM to generate the stack frame data
-      ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
-      ClassVisitor v2 = cw;
+    // v = new CheckClassAdapter(v);
+    // v = new TraceClassVisitor(v, new PrintWriter(System.out));
 
-      // Upgrade to version 50 class format
-      v2 = new ClassAdapter(v2) {
-        @Override
-        public void visit(int version, int access, String name,
-            String signature, String superName, String[] interfaces) {
-          super.visit(Opcodes.V1_6, access, name, signature, superName,
-              interfaces);
-        }
-      };
-
-      new ClassReader(classBytes).accept(v2, 0);
-      classBytes = cw.toByteArray();
+    String[] interfaces;
+    // TODO(bov): something better than linear?
+    if (superDescs.contains("java/lang/Object")) {
+      interfaces = null;
+    } else {
+      interfaces = superDescs.toArray(new String[superDescs.size()]);
     }
-    return classBytes;
+    v.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC | Opcodes.ACC_INTERFACE, desc,
+        null, "java/lang/Object", interfaces);
+    v.visitEnd();
+    return writer.toByteArray();
   }
 }
diff --git a/dev/core/src/com/google/gwt/dev/shell/rewrite/OriginalJsniSignature.java b/dev/core/src/com/google/gwt/dev/shell/rewrite/OriginalJsniSignature.java
deleted file mode 100644
index 74d924a..0000000
--- a/dev/core/src/com/google/gwt/dev/shell/rewrite/OriginalJsniSignature.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright 2010 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.dev.shell.rewrite;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * This annotation will be applied to a method or constructor by
- * {@link HostedModeClassRewriter} if the method signature of a rewritten method
- * differs from the original source. This annotation is necessary because
- * dispatch id allocation is performed reflectively, after the class rewriting
- * may have altered the method.
- */
-@Retention(RetentionPolicy.RUNTIME)
-@Target(value = {ElementType.CONSTRUCTOR, ElementType.METHOD})
-public @interface OriginalJsniSignature {
-  String name();
-
-  /**
-   * This is the method descriptor, minus the return type.
-   */
-  String paramList();
-}
diff --git a/dev/core/src/com/google/gwt/dev/shell/rewrite/RewriteJsoArrays.java b/dev/core/src/com/google/gwt/dev/shell/rewrite/RewriteJsoArrays.java
deleted file mode 100644
index db716e3..0000000
--- a/dev/core/src/com/google/gwt/dev/shell/rewrite/RewriteJsoArrays.java
+++ /dev/null
@@ -1,388 +0,0 @@
-/*
- * Copyright 2010 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.dev.shell.rewrite;
-
-import static com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter.DISAMBIGUATOR_TYPE_INTERNAL_NAME;
-import static com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter.SYSTEM_CLASS_VERSION;
-
-import com.google.gwt.dev.asm.AnnotationVisitor;
-import com.google.gwt.dev.asm.ClassAdapter;
-import com.google.gwt.dev.asm.ClassVisitor;
-import com.google.gwt.dev.asm.ClassWriter;
-import com.google.gwt.dev.asm.FieldVisitor;
-import com.google.gwt.dev.asm.Label;
-import com.google.gwt.dev.asm.MethodVisitor;
-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.shell.rewrite.HostedModeClassRewriter.RewriterOracle;
-import com.google.gwt.dev.util.Name.InternalName;
-
-import java.util.Arrays;
-
-/**
- * The object of this pass is to turn all JSO subtype arrays into
- * JavaScriptObject arrays. This allows JSO arrays to participate in arbitrary
- * cross-casting.
- */
-public class RewriteJsoArrays extends ClassAdapter {
-
-  private static final String ORIGINAL_JSNI_SIGNATURE_DESC = Type.getDescriptor(OriginalJsniSignature.class);
-
-  /**
-   * Performs upcasts on JSO subtype arrays.
-   */
-  private class UpcastAdapter extends DebugAnalyzerAdapter {
-
-    private final int minLocals;
-    private int minStack;
-
-    public UpcastAdapter(String owner, int access, String name, String desc,
-        MethodVisitor mv, int minLocals) {
-      super(owner, access, name, desc, mv);
-      this.minLocals = minLocals;
-    }
-
-    /**
-     * Upcast field access.
-     */
-    @Override
-    public void visitFieldInsn(int opcode, String owner, String name,
-        String desc) {
-      Type t = Type.getType(desc);
-      if (t.getSort() == Type.ARRAY) {
-        t = upcastJsoType(t);
-        if (t != null) {
-          desc = t.getDescriptor();
-        }
-      }
-      super.visitFieldInsn(opcode, owner, name, desc);
-    }
-
-    /**
-     * Fix JSO array types in framing data to keep the verifier and other
-     * AnalyzerAdapters synced up.
-     */
-    @Override
-    public void visitFrame(int type, int nLocal, Object[] local, int nStack,
-        Object[] stack) {
-      fixFrameData(local);
-      fixFrameData(stack);
-      super.visitFrame(type, nLocal, local, nStack, stack);
-    }
-
-    /**
-     * Keep the debugger from going off the rails.
-     */
-    @Override
-    public void visitLocalVariable(String name, String desc, String signature,
-        Label start, Label end, int index) {
-      Type t = Type.getType(desc);
-      if (t.getSort() == Type.ARRAY) {
-        t = upcastJsoType(t);
-        if (t != null) {
-          desc = t.getInternalName();
-        }
-      }
-      super.visitLocalVariable(name, desc, signature, start, end, index);
-    }
-
-    /**
-     * We need to override the number of locals when rewriting a constructor to
-     * account for synthetic disambiguation parameters. The null argument to the
-     * disambiguated constructor might require adjustment of the stack size.
-     */
-    @Override
-    public void visitMaxs(int maxStack, int maxLocals) {
-      super.visitMaxs(Math.max(minStack, maxStack), Math.max(minLocals,
-          maxLocals));
-    }
-
-    /**
-     * Fix the descriptors used by method invocations to account for the upcast
-     * array types. Initializers are handled specially, since we can't simply
-     * change their name to provide disambiguation. Instead, we add an
-     * additional null argument of a synthetic type.
-     */
-    @Override
-    public void visitMethodInsn(int opcode, String owner, String name,
-        String desc) {
-      switch (opcode) {
-        case Opcodes.INVOKESPECIAL: {
-          if ("<init>".equals(name)) {
-            String[] disambiguator = rewriterOracle.getArrayDisambiguator(desc);
-            // Widen any JSO arrays.
-            Method m = upcastMethod(name, desc);
-            desc = m == null ? desc : m.getDescriptor();
-            if (disambiguator.length != 0) {
-              desc = addDisambiguator(desc, disambiguator);
-              // Add bogus values to the stack
-              recordDebugData("Constructor disambiguation "
-                  + Arrays.asList(disambiguator));
-              for (int i = 0, j = disambiguator.length; i < j; i++) {
-                super.visitInsn(Opcodes.ACONST_NULL);
-              }
-              minStack = Math.max(minStack, stack.size());
-            }
-            break;
-          }
-          // Intentional fallthrough for non-init methods
-        }
-        case Opcodes.INVOKEINTERFACE:
-        case Opcodes.INVOKESTATIC:
-        case Opcodes.INVOKEVIRTUAL: {
-          Method m = upcastMethod(name, desc);
-          if (m != null) {
-            recordDebugData("Replaced method call " + name + " " + desc
-                + " with " + m.toString());
-            name = m.getName();
-            desc = m.getDescriptor();
-          }
-          break;
-        }
-        default:
-          throw new RuntimeException("Unhandled method instruction " + opcode);
-      }
-      super.visitMethodInsn(opcode, owner, name, desc);
-    }
-
-    /**
-     * Fix the types of multidimensional arrays allocations.
-     */
-    @Override
-    public void visitMultiANewArrayInsn(String desc, int dims) {
-      Type replacement = upcastJsoType(Type.getObjectType(desc));
-      if (replacement != null) {
-        recordDebugData("Widened JSO array allocation");
-        desc = replacement.getInternalName();
-      }
-      super.visitMultiANewArrayInsn(desc, dims);
-    }
-
-    /**
-     * Convert all JSO subtype array allocations into an allocation of a
-     * JavaScriptObject array.
-     */
-    @Override
-    public void visitTypeInsn(int opcode, String internalName) {
-      if (opcode == Opcodes.ANEWARRAY) {
-        Type replacement = upcastJsoType(Type.getObjectType(internalName));
-        if (replacement != null) {
-          recordDebugData("Widened JSO array allocation");
-          internalName = replacement.getInternalName();
-        }
-      }
-      super.visitTypeInsn(opcode, internalName);
-    }
-
-    /**
-     * Utility method used by {@link #visitFrame} to process the stack and local
-     * arrays.
-     */
-    private void fixFrameData(Object[] data) {
-      for (int i = 0, j = data.length; i < j; i++) {
-        if (data[i] instanceof String) {
-          Type t = Type.getObjectType((String) data[i]);
-          if (t.getSort() == Type.ARRAY) {
-            t = upcastJsoType(t);
-            if (t != null) {
-              data[i] = t.getInternalName();
-            }
-          }
-        }
-      }
-    }
-  }
-
-  /**
-   * Creates an empty interface type.
-   */
-  static byte[] writeConstructorDisambiguationType(String className) {
-    String internalName = className.replace('.', '/');
-    assert internalName.startsWith(DISAMBIGUATOR_TYPE_INTERNAL_NAME) : "Bad className "
-        + className;
-
-    ClassWriter writer = new ClassWriter(0);
-
-    writer.visit(SYSTEM_CLASS_VERSION, Opcodes.ACC_PUBLIC
-        | Opcodes.ACC_ABSTRACT | Opcodes.ACC_INTERFACE, internalName, null,
-        "java/lang/Object", null);
-
-    writer.visitEnd();
-    return writer.toByteArray();
-  }
-
-  /**
-   * Records the class currently being processed.
-   */
-  private String owner;
-  private final RewriterOracle rewriterOracle;
-
-  public RewriteJsoArrays(ClassVisitor v, RewriterOracle rewriterOracle) {
-    super(v);
-    this.rewriterOracle = rewriterOracle;
-  }
-
-  @Override
-  public void visit(int version, int access, String name, String signature,
-      String superName, String[] interfaces) {
-    super.visit(version, access, name, signature, superName, interfaces);
-    owner = name;
-  }
-
-  /**
-   * Widen all fields of a JSO array type to JSO[].
-   */
-  @Override
-  public FieldVisitor visitField(int access, String name, String desc,
-      String signature, Object value) {
-    Type t = Type.getType(desc);
-    if (t.getSort() == Type.ARRAY) {
-      Type newType = upcastJsoType(t);
-      if (newType != null) {
-        desc = newType.getDescriptor();
-      }
-    }
-    return super.visitField(access, name, desc, signature, value);
-  }
-
-  /**
-   * Widen all JSO array paramaters to JSO[]. Constructors may have a
-   * disambiguator type added to their parameter list.
-   */
-  @Override
-  public MethodVisitor visitMethod(int access, String name, String desc,
-      String signature, String[] exceptions) {
-    int minLocals = 0;
-    String originalJsniName = name;
-    String originalJsniDesc = desc;
-    Method upcast = upcastMethod(name, desc);
-
-    if (upcast != null) {
-      if ("<init>".equals(name)) {
-        String[] disambiguator = rewriterOracle.getArrayDisambiguator(desc);
-        desc = upcast.getDescriptor();
-        if (disambiguator.length != 0) {
-          desc = addDisambiguator(desc, disambiguator);
-          // +1 for this
-          minLocals = Type.getArgumentTypes(desc).length + 1;
-        }
-        originalJsniName = "new";
-      } else {
-        name = upcast.getName();
-        desc = upcast.getDescriptor();
-      }
-    }
-
-    MethodVisitor mv = super.visitMethod(access, name, desc, signature,
-        exceptions);
-    if (mv != null) {
-      // Record the original JSNI signature if we've mangled the method
-      if (upcast != null) {
-        AnnotationVisitor av = mv.visitAnnotation(ORIGINAL_JSNI_SIGNATURE_DESC,
-            true);
-        if (av != null) {
-          av.visit("name", originalJsniName);
-          // Strip the return type
-          av.visit("paramList", originalJsniDesc.substring(0,
-              originalJsniDesc.indexOf(')') + 1));
-          av.visitEnd();
-        }
-      }
-      mv = new UpcastAdapter(owner, access, name, desc, mv, minLocals);
-    }
-    return mv;
-  }
-
-  /**
-   * Add disambiguator types as the last parameters of a method descriptor.
-   */
-  private String addDisambiguator(String desc, String[] disambiguator) {
-    int idx = desc.indexOf(')');
-    StringBuilder sb = new StringBuilder();
-    sb.append(desc.substring(0, idx));
-    for (String d : disambiguator) {
-      sb.append("L").append(d).append(";");
-    }
-    sb.append(desc.substring(idx));
-    return sb.toString();
-  }
-
-  /**
-   * Utility method for constructing a descriptor. If the given type is a JSO
-   * subtype array, the appropriate JavaScriptObject array type will be appended
-   * to the descriptor and this method will return <code>true</code>. Otherwise,
-   * this method will simply append the type to the descriptor and return
-   * <code>false</code>.
-   */
-  private boolean appendTypeMaybeUpcast(StringBuilder newDesc, Type type) {
-    if (type.getSort() == Type.ARRAY) {
-      Type newType = upcastJsoType(type);
-      if (newType != null) {
-        newDesc.append(newType.getDescriptor());
-        return true;
-      }
-    }
-    newDesc.append(type.getDescriptor());
-    return false;
-  }
-
-  /**
-   * Calls {@link RewriteJsoCasts#upcastJsoType} with the instance's
-   * {@link #rewriterOracle}.
-   */
-  private Type upcastJsoType(Type type) {
-    return RewriteJsoCasts.upcastJsoType(rewriterOracle, type);
-  }
-
-  /**
-   * Determine if a descriptor contains references to JSO subtype arrays. If so,
-   * returns an upcast descriptor and a guaranteed-unique name for the method.
-   * Otherwise, this method returns <code>null</code>.
-   */
-  private Method upcastMethod(String name, String desc) {
-    boolean didChange = false;
-    StringBuilder newName = new StringBuilder(name);
-    StringBuilder newDesc = new StringBuilder("(");
-
-    for (Type arg : Type.getArgumentTypes(desc)) {
-      // Add the arguments, one at a time, to the new descriptor
-      if (appendTypeMaybeUpcast(newDesc, arg)) {
-        didChange = true;
-        /*
-         * Add the original type to the method name. We don't need to worry
-         * about the number of parameters or their relative positions when
-         * constructing the name, because that information is still in the
-         * method descriptor.
-         */
-        newName.append("_").append(
-            InternalName.toIdentifier(arg.getElementType().getInternalName())).append(
-            "_").append(arg.getDimensions());
-      }
-    }
-    newDesc.append(")");
-
-    Type returnType = Type.getReturnType(desc);
-    if (appendTypeMaybeUpcast(newDesc, returnType)) {
-      didChange = true;
-    }
-
-    if (!didChange) {
-      return null;
-    }
-    return new Method(newName.toString(), newDesc.toString());
-  }
-}
diff --git a/dev/core/src/com/google/gwt/dev/shell/rewrite/RewriteJsoCasts.java b/dev/core/src/com/google/gwt/dev/shell/rewrite/RewriteJsoCasts.java
deleted file mode 100644
index 267fe42..0000000
--- a/dev/core/src/com/google/gwt/dev/shell/rewrite/RewriteJsoCasts.java
+++ /dev/null
@@ -1,557 +0,0 @@
-/*
- * Copyright 2010 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.dev.shell.rewrite;
-
-import static com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter.CANONICAL_FIELD;
-import static com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter.JAVASCRIPTOBJECT_DESC;
-import static com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter.REWRAP_METHOD;
-import static com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter.SINGLE_JSO_IMPL_ADJUNCT_SUFFIX;
-import static com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter.SINGLE_JSO_IMPL_CAST_METHOD;
-import static com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter.SINGLE_JSO_IMPL_CAST_TO_OBJECT_METHOD;
-import static com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter.SINGLE_JSO_IMPL_INSTANCEOF_METHOD;
-import static com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter.SINGLE_JSO_IMPL_SUPPORT_CLASS;
-
-import com.google.gwt.dev.asm.ClassAdapter;
-import com.google.gwt.dev.asm.ClassVisitor;
-import com.google.gwt.dev.asm.Label;
-import com.google.gwt.dev.asm.MethodVisitor;
-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.shell.rewrite.HostedModeClassRewriter.RewriterOracle;
-
-/**
- * Ensure that an appropriate JSO wrapper type or canonical JavaScriptObject is
- * used. Assignments to fields, locals, arrays, return statements, and method
- * parameters will be updated with the following rules:
- * <ul>
- * <li>Changing from one JSO type to another results in a replacement JSO
- * Wrapper type being placed on the stack</li>
- * <li>Referring to a JSO or a JSO subype as Object results in the canonical JSO
- * instance being placed on the stack</li>
- * <li>An invocation of <code>getClass()</code> on a JSO subtype results in
- * <code>JavaScriptObject.class</code> being placed on the stack.</li>
- * </ul>
- */
-class RewriteJsoCasts extends ClassAdapter {
-
-  /**
-   * Injects necessary casting logic.
-   */
-  private class MyMethodAdapter extends DebugAnalyzerAdapter {
-    /*
-     * NOTE TO MAINTAINERS: It's tempting to sprinkle one-off cast-handling
-     * logic around this class. That way lies madness while trying to track down
-     * exactly how a particular bit of cast logic wound up in the bytecode.
-     * Instead, all casting logic should be routed through generateCast.
-     */
-
-    private final boolean returnNeedsCanonical;
-    private final Type returnType;
-
-    public MyMethodAdapter(int access, String name, String desc,
-        MethodVisitor mv) {
-      super(currentClass, access, name, desc, mv);
-
-      Method m = new Method(name, desc);
-      returnType = m.getReturnType();
-      if (returnType.getSort() == Type.OBJECT) {
-        String internalName = returnType.getInternalName();
-        boolean maybeCanonicalize = rewriterOracle.jsoAssignmentRequiresCanonicalization(internalName);
-        boolean maybeRewrap = rewriterOracle.isJsoOrSubtype(internalName);
-        returnNeedsCanonical = maybeCanonicalize || maybeRewrap;
-      } else {
-        returnNeedsCanonical = false;
-      }
-    }
-
-    @Override
-    public void visitFieldInsn(int opcode, String owner, String name,
-        String desc) {
-      if (desc.charAt(0) == 'L' && !owner.equals(JAVASCRIPTOBJECT_DESC)
-          && !name.equals(CANONICAL_FIELD)) {
-        switch (opcode) {
-          case Opcodes.PUTFIELD:
-          case Opcodes.PUTSTATIC:
-            generateCast(desc.substring(1, desc.length() - 1));
-        }
-      }
-      super.visitFieldInsn(opcode, owner, name, desc);
-    }
-
-    /**
-     * Possibly canonicalize returned values.
-     */
-    @Override
-    public void visitInsn(int opcode) {
-      if (opcode == Opcodes.ARETURN && returnNeedsCanonical) {
-        // Stack is: returnValue
-        generateCast(returnType.getInternalName());
-        // Stack is: canonical
-      } else if (opcode == Opcodes.AASTORE) {
-        // Stack is: ..., arrayRef, index, value
-        Object topType = stack.get(stack.size() - 1);
-        if (topType instanceof String) {
-          String objectType = (String) topType;
-          String arrayType = (String) stack.get(stack.size() - 3);
-          Type t = Type.getObjectType(arrayType);
-          if (t.getDimensions() == 1
-              && rewriterOracle.jsoAssignmentRequiresCanonicalization(objectType)) {
-            /*
-             * If the object being assigned needs canonicalization, then we want
-             * to ensure the canonical object is being stored in the array.
-             */
-            generateCast(objectType);
-          }
-        }
-      }
-      super.visitInsn(opcode);
-    }
-
-    /**
-     * Replace references to JSO subtype class literals with references to
-     * JavaScriptObject.
-     */
-    @Override
-    public void visitLdcInsn(Object cst) {
-      if (cst instanceof Type) {
-        Type upcast = upcastJsoType((Type) cst);
-        if (upcast != null) {
-          cst = upcast;
-        }
-      }
-      super.visitLdcInsn(cst);
-    }
-
-    /**
-     * Canonicalize the arguments to methods that require Object or
-     * JavaScriptObject as parameters. Additional local variables will be
-     * allocated to save as many of the arguments as necessary in order to cast
-     * the first parameter that requires canonicalization.
-     */
-    @Override
-    public void visitMethodInsn(int opcode, String owner, String name,
-        String desc) {
-
-      // Update calls to getClass()
-      if ("getClass".equals(name) && "()Ljava/lang/Class;".equals(desc)) {
-        // Stack is: instance
-        Type t = Type.getObjectType((String) stack.get(stack.size() - 1));
-        if (t.getSort() == Type.OBJECT
-            && rewriterOracle.couldContainJso(t.getInternalName())) {
-          /*
-           * Make sure the canonical object is on the stack. This method call
-           * will pass nulls straight through, so the getClass() invocation will
-           * still trigger an NPE.
-           */
-          recordDebugData("Canonicalized object for getClass");
-          super.visitMethodInsn(Opcodes.INVOKESTATIC,
-              SINGLE_JSO_IMPL_SUPPORT_CLASS, "ensureCanonical",
-              "(Ljava/lang/Object;)Ljava/lang/Object;");
-          // Stack is: canonical object
-          super.visitMethodInsn(opcode, "java/lang/Object", name, desc);
-          // Stack is: classLit
-          return;
-        }
-      } else if ("cast".equals(name)
-          && ("()L" + JAVASCRIPTOBJECT_DESC + ";").equals(desc)
-          && rewriterOracle.isJsoOrSubtype(owner)) {
-        // Remove calls to cast, since they trigger NPEs in older code
-        recordDebugData("Removed call to JSO.cast()");
-        return;
-      }
-
-      /*
-       * We want to ensure that all arguments passed into the method have been
-       * canonicalized. To do this, we'll find the first argument on the stack
-       * that requires canonicalization. Each argument on above the first
-       * argument on the stack will be stored in a temporary local variable and
-       * eventually restored.
-       * 
-       * Assume the stack looks like this:
-       * 
-       * instance, Ok, NeedsCanonicalization, Ok, Ok, NeedsCanonicalization
-       * 
-       * (1) We'll store the top three arguments in locals until we have this:
-       * 
-       * instance, Ok, NeedsCanonicalization
-       * 
-       * (2) Then, we'll apply whatever canonicalization is necessary to the
-       * top-most element:
-       * 
-       * instance, Ok, Canonicalized
-       * 
-       * (3) The arguments stored in the temporary local variables will be
-       * pushed back onto the stack, possibly canonicalizing them in the
-       * process:
-       * 
-       * instance, Ok, Canonicalized, Ok, Ok, Canonicalized
-       * 
-       * This could be accomplished without the use of local variables if we
-       * were to track the last opcode that pushes an argument value onto the
-       * stack and buffer intervening opcodes or use a two-pass approach. Once
-       * the relevant opcode has been identified, the canonicalization logic
-       * could be immediately inserted.
-       */
-      Method m = new Method(name, desc);
-      boolean isInstance = opcode == Opcodes.INVOKEINTERFACE
-          || opcode == Opcodes.INVOKEVIRTUAL;
-      Type[] args;
-      if (isInstance) {
-        /*
-         * Treat invocations of non-static dispatch as though they were static
-         * methods that have an extra "this" argument that is the type of the
-         * instance that's expected.
-         */
-        Type[] actualArgs = m.getArgumentTypes();
-        args = new Type[actualArgs.length + 1];
-        args[0] = Type.getObjectType(owner);
-        System.arraycopy(actualArgs, 0, args, 1, actualArgs.length);
-      } else {
-        args = m.getArgumentTypes();
-      }
-      int firstCast = -1;
-      boolean[] needsCanonicalization = new boolean[args.length];
-
-      // (1) Works backwards, since the last argument is on top of the stack
-      for (int i = args.length - 1, s = stack.size(); i >= 0; i--) {
-        Type arg = args[i];
-        s -= arg.getSize();
-
-        /*
-         * Ignore arguments that never require a canonicalizing cast or
-         * rewrapping.
-         */
-        if (arg.getSort() != Type.OBJECT) {
-          continue;
-        }
-        boolean maybeCanonicalize = rewriterOracle.jsoAssignmentRequiresCanonicalization(arg.getInternalName());
-        boolean maybeRewrap = rewriterOracle.couldContainJso(arg.getInternalName());
-        if (!maybeCanonicalize && !maybeRewrap) {
-          continue;
-        }
-
-        Object onStack = stack.get(s);
-        assert !onStack.equals(Opcodes.TOP) : "Bad offset computation";
-
-        if (onStack instanceof String) {
-          String stackType = (String) onStack;
-          if (rewriterOracle.couldContainJso(stackType)) {
-            needsCanonicalization[i] = true;
-            firstCast = i;
-          }
-        }
-      }
-
-      // Short-circuit if none of the arguments need canonicalization
-      if (firstCast >= 0) {
-        recordDebugData("Fixed arguments to method invocation");
-        // These labels are used to provide information to the debugger
-        Label castStackStart = new Label();
-        Label castStackEnd = new Label();
-
-        // (1) Pop N - 1 arguments off of the stack, storing them in locals
-        super.visitLabel(castStackStart);
-        int[] slots = new int[args.length];
-        for (int i = args.length - 1; i >= firstCast + 1; i--) {
-          Type arg = args[i];
-          int slot = locals.size();
-          slots[i] = slot;
-          super.visitVarInsn(arg.getOpcode(Opcodes.ISTORE), slot);
-        }
-
-        // (2) Convert the first argument
-        generateCast(args[firstCast].getInternalName());
-
-        // (3) Load arguments, converting those that need them
-        for (int i = firstCast + 1; i < args.length; i++) {
-          Type arg = args[i];
-          int slot = slots[i];
-          super.visitVarInsn(arg.getOpcode(Opcodes.ILOAD), slot);
-          if (needsCanonicalization[i]) {
-            generateCast(arg.getInternalName());
-          }
-        }
-
-        super.visitLabel(castStackEnd);
-
-        // Record locals to aid debbuging and manual inspection of bytecode.
-        for (int i = firstCast + 1; i < args.length; i++) {
-          super.visitLocalVariable("$cast_" + i, args[i].getDescriptor(), null,
-              castStackStart, castStackEnd, slots[i]);
-        }
-
-        // Record new stack / locals size
-        super.visitMaxs(stack.size(), locals.size());
-      }
-
-      // Call the method
-      super.visitMethodInsn(opcode, owner, name, desc);
-    }
-
-    /**
-     * Rewrite casts and instanceof tests.
-     */
-    @Override
-    public void visitTypeInsn(int opcode, String type) {
-      Type parsed = Type.getObjectType(type);
-      switch (opcode) {
-        case Opcodes.CHECKCAST:
-          if (rewriterOracle.couldContainJso(type)) {
-            // Maybe generate casting code for JSO types
-            if (generateCast(type)) {
-              return;
-            }
-          } else if (parsed.getSort() == Type.ARRAY) {
-            // Always upcast JSO array types
-            Type t = upcastJsoType(parsed);
-            if (t != null) {
-              recordDebugData("Upcast JSO array checkcast from "
-                  + parsed.getInternalName());
-              type = t.getInternalName();
-            }
-          }
-          // Intentional fall-through to super invocation below
-          break;
-
-        case Opcodes.INSTANCEOF: {
-          String internalName = parsed.getInternalName();
-          if (rewriterOracle.isInterface(internalName)
-              && rewriterOracle.couldContainJso(internalName)) {
-            recordDebugData("SingleJsoImpl instanceof check for "
-                + internalName);
-            /*
-             * We need to use the interface's adjunct type if the interface
-             * could contain a JSO type.
-             */
-            super.visitMethodInsn(Opcodes.INVOKESTATIC, type
-                + SINGLE_JSO_IMPL_ADJUNCT_SUFFIX,
-                SINGLE_JSO_IMPL_INSTANCEOF_METHOD, "(Ljava/lang/Object;)Z");
-            return;
-          }
-          // Always try to upcast instanceof JSO tests
-          recordDebugData("Upcast instanceof from " + internalName);
-          Type t = upcastJsoType(parsed);
-          if (t != null) {
-            type = t.getInternalName();
-          }
-          break;
-        }
-      }
-      super.visitTypeInsn(opcode, type);
-    }
-
-    /**
-     * The do-everything method for determining how to make whatever is on top
-     * of the stack fit correctly in a slot of the given type. Casts to Object
-     * should result in the canonical object winding up on the stack. Otherwise,
-     * we may need a new wrapper instance.
-     * <p>
-     * This method should generate code that is stack-neutral. Any non-trivial
-     * cast logic should be moved to a support class, either
-     * {@link SingleJsoImplSupport} or the synthetic interface adjunct classes.
-     * 
-     * @param internalName the type to which the object on top of the stack
-     *          should be assigned
-     * @return <code>true</code> if cast code was generated
-     */
-    private boolean generateCast(String internalName) {
-      assert internalName.charAt(0) != 'L'
-          && internalName.charAt(internalName.length() - 1) != ';' : "Passed "
-          + "a descriptor instead of an internal name: " + internalName;
-      Object topStack = stack.get(stack.size() - 1);
-      if (!(topStack instanceof String)) {
-        return false;
-      }
-
-      final String topType = (String) topStack;
-
-      recordDebugData(topType + " ->  " + internalName);
-
-      if (internalName.equals("java/lang/Object")) {
-        /*
-         * We're looking at code (probably created by one of our other visitors)
-         * that is casting a value to java.lang.Object.
-         */
-        // Stack is: something
-
-        if (rewriterOracle.isJsoOrSubtype(topType)) {
-          /*
-           * We know that the top of the stack is a wrapper type, so we need to
-           * canonicalize it by calling JavaScriptObject's synthetic rewrap
-           * method.
-           */
-          // Stack is: jsoSubtype
-          super.visitMethodInsn(Opcodes.INVOKESTATIC, JAVASCRIPTOBJECT_DESC,
-              REWRAP_METHOD, "(L" + JAVASCRIPTOBJECT_DESC + ";)L"
-                  + JAVASCRIPTOBJECT_DESC + ";");
-          // Stack is: canonicalObject
-          return true;
-
-        } else if (rewriterOracle.isInterface(topType)
-            && rewriterOracle.couldContainJso(topType)) {
-          /*
-           * We're looking at an interface that may contain a JSO. If the object
-           * is a JSO, replace it with the canonical instance by calling into
-           * the interface's SingleJsoImpl adjunct class's castToObject()
-           * method.
-           */
-          String supportType = topType + SINGLE_JSO_IMPL_ADJUNCT_SUFFIX;
-
-          // Stack is: interfaceType
-          super.visitMethodInsn(Opcodes.INVOKESTATIC, supportType,
-              SINGLE_JSO_IMPL_CAST_TO_OBJECT_METHOD, "(L" + topType
-                  + ";)Ljava/lang/Object;");
-          // Stack is: object (possible canonical JSO)
-          return true;
-
-        } else if ("java/lang/Object".equals(topType)) {
-          /*
-           * This is a situation that we'll run into when dealing with JRE code.
-           * See JsoTest.testArrayJreInteractions for the canonical example of
-           * loss of type information.
-           */
-          super.visitMethodInsn(Opcodes.INVOKESTATIC,
-              SINGLE_JSO_IMPL_SUPPORT_CLASS, "ensureCanonical",
-              "(Ljava/lang/Object;)Ljava/lang/Object;");
-          // Stack is: canonical object
-          return true;
-
-        } else {
-          // Casting some non-JSO type to Object
-          return false;
-        }
-
-      } else if (internalName.equals(topType)) {
-        // Casting an object to its own type
-        return false;
-
-      } else if (!rewriterOracle.couldContainJso(internalName)) {
-        // It's a cast to something that could never contain a JSO
-        return false;
-
-      } else if (rewriterOracle.isInterface(internalName)
-          && rewriterOracle.couldContainJso(internalName)) {
-        /*
-         * Casting to a SingleJsoImpl interface. Need to use the SingleJsoImpl
-         * adjunct to possibly create a new wrapper type.
-         */
-
-        // Stack is: something
-        super.visitMethodInsn(Opcodes.INVOKESTATIC, internalName
-            + SINGLE_JSO_IMPL_ADJUNCT_SUFFIX, SINGLE_JSO_IMPL_CAST_METHOD,
-            "(Ljava/lang/Object;)Ljava/lang/Object;");
-        // Stack is: something (maybe JSO wrapper type)
-        super.visitTypeInsn(Opcodes.CHECKCAST, internalName);
-        // Stack is: desiredType (maybe JSO wrapper type)
-
-        return true;
-
-      } else if (rewriterOracle.isJsoOrSubtype(internalName)) {
-        /*
-         * Casting to a JavaScriptObject subtype.
-         */
-
-        /*
-         * Change the cast to JavaScriptObject to ensure we get a
-         * ClassCastException for something like (JsoSubclass) "foo".
-         */
-        // Stack is: something
-        super.visitTypeInsn(Opcodes.CHECKCAST, JAVASCRIPTOBJECT_DESC);
-        // Stack is: jso
-
-        /*
-         * Put the canonical object onto the stack by calling
-         * JsoSubclass.$rewrap().
-         */
-        super.visitMethodInsn(Opcodes.INVOKESTATIC, internalName,
-            REWRAP_METHOD, "(L" + JAVASCRIPTOBJECT_DESC + ";)L" + internalName
-                + ";");
-        // Stack is: wrapperObject
-        return true;
-      }
-
-      /*
-       * Fail definitively, since getting this wrong is going to break the user
-       * in all kinds of unpredictable ways.
-       */
-      throw new RuntimeException(
-          "generateCast was called with an unhandled configuration. topType: "
-              + topType + " internalName: " + internalName);
-    }
-  }
-
-  /**
-   * If <code>type</code> is a JSO subtype or JSO subtype array, return the JSO
-   * type or JSO array type to which <code>type</code> can be assigned, or
-   * <code>null</code>.
-   */
-  static Type upcastJsoType(RewriterOracle rewriterOracle, Type type) {
-    StringBuilder sb = new StringBuilder();
-    if (type.getSort() == Type.ARRAY) {
-      Type elementType = type.getElementType();
-      if (elementType.getSort() != Type.OBJECT
-          || !rewriterOracle.isJsoOrSubtype(elementType.getInternalName())) {
-        return null;
-      }
-      for (int i = 0, j = type.getDimensions(); i < j; i++) {
-        sb.append("[");
-      }
-    } else if (type.getSort() == Type.OBJECT) {
-      if (type.getInternalName().equals(JAVASCRIPTOBJECT_DESC)
-          || !rewriterOracle.isJsoOrSubtype(type.getInternalName())) {
-        return null;
-      }
-    } else {
-      return null;
-    }
-    sb.append("L" + JAVASCRIPTOBJECT_DESC + ";");
-    return Type.getType(sb.toString());
-  }
-
-  private String currentClass;
-  private final RewriterOracle rewriterOracle;
-
-  public RewriteJsoCasts(ClassVisitor v, RewriterOracle rewriterOracle) {
-    super(v);
-    this.rewriterOracle = rewriterOracle;
-  }
-
-  @Override
-  public void visit(int version, int access, String name, String signature,
-      String superName, String[] interfaces) {
-    currentClass = name;
-    super.visit(version, access, name, signature, superName, interfaces);
-  }
-
-  @Override
-  public MethodVisitor visitMethod(int access, String name, String desc,
-      String signature, String[] exceptions) {
-    MethodVisitor mv = super.visitMethod(access, name, desc, signature,
-        exceptions);
-    if (mv != null && !REWRAP_METHOD.equals(name)) {
-      mv = new MyMethodAdapter(access, name, desc, mv);
-    }
-    return mv;
-  }
-
-  /**
-   * Convenience method.
-   */
-  private Type upcastJsoType(Type type) {
-    return upcastJsoType(rewriterOracle, type);
-  }
-}
diff --git a/dev/core/src/com/google/gwt/dev/shell/rewrite/RewriteObjectComparisons.java b/dev/core/src/com/google/gwt/dev/shell/rewrite/RewriteObjectComparisons.java
deleted file mode 100644
index f7d2377..0000000
--- a/dev/core/src/com/google/gwt/dev/shell/rewrite/RewriteObjectComparisons.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright 2010 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.dev.shell.rewrite;
-
-import com.google.gwt.dev.asm.ClassAdapter;
-import com.google.gwt.dev.asm.ClassVisitor;
-import com.google.gwt.dev.asm.Label;
-import com.google.gwt.dev.asm.MethodVisitor;
-import com.google.gwt.dev.asm.Opcodes;
-import com.google.gwt.dev.asm.commons.AnalyzerAdapter;
-import com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter.RewriterOracle;
-
-/**
- * This injects artificial casts to Object which will be replaced by
- * {@link RewriteJsoCasts}.
- */
-class RewriteObjectComparisons extends ClassAdapter {
-
-  private class MyMethodAdapter extends AnalyzerAdapter {
-    public MyMethodAdapter(String owner, int access, String name, String desc,
-        MethodVisitor mv) {
-      super(owner, access, name, desc, mv);
-    }
-
-    /**
-     * All object equality comparisons in the JVM are performed via a jump
-     * opcode. Even something as simple as <code>boolean x = a == b;</code> is
-     * implemented as
-     * 
-     * <pre>
-     * ALOAD 1;
-     * ALOAD 2;
-     * IF_ACMPEQ label;
-     * PUSH false;
-     * GOTO: done;
-     * label: PUSH true;
-     * done: ASTORE 3;
-     * </pre>
-     */
-    @Override
-    public void visitJumpInsn(int opcode, Label label) {
-      switch (opcode) {
-        case Opcodes.IF_ACMPEQ:
-        case Opcodes.IF_ACMPNE:
-          Object type1 = stack.get(stack.size() - 2);
-          boolean jso1 = type1 instanceof String
-              && rewriterOracle.couldContainJso((String) type1);
-          Object type2 = stack.get(stack.size() - 1);
-          boolean jso2 = type2 instanceof String
-              && rewriterOracle.couldContainJso((String) type2);
-
-          if (jso1 || jso2) {
-            if (jso2) {
-              // Stack: something, something
-              super.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Object");
-            }
-            if (jso1) {
-              // Stack: something, object2
-              super.visitInsn(Opcodes.SWAP);
-              // Stack: object2, something
-              super.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Object");
-              // Stack: object2, object1
-            }
-          }
-      }
-      super.visitJumpInsn(opcode, label);
-    }
-  }
-
-  private final RewriterOracle rewriterOracle;
-  private String currentClass;
-
-  public RewriteObjectComparisons(ClassVisitor v, RewriterOracle rewriterOracle) {
-    super(v);
-    this.rewriterOracle = rewriterOracle;
-  }
-
-  @Override
-  public void visit(int version, int access, String name, String signature,
-      String superName, String[] interfaces) {
-    currentClass = name;
-    super.visit(version, access, name, signature, superName, interfaces);
-  }
-
-  @Override
-  public MethodVisitor visitMethod(int access, String name, String desc,
-      String signature, String[] exceptions) {
-    MethodVisitor mv = super.visitMethod(access, name, desc, signature,
-        exceptions);
-    if (mv != null) {
-      mv = new MyMethodAdapter(currentClass, access, name, desc, mv);
-    }
-    return mv;
-  }
-}
diff --git a/dev/core/src/com/google/gwt/dev/shell/rewrite/RewriteRefsToJsoClasses.java b/dev/core/src/com/google/gwt/dev/shell/rewrite/RewriteRefsToJsoClasses.java
new file mode 100644
index 0000000..3a45363
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/shell/rewrite/RewriteRefsToJsoClasses.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2008 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.dev.shell.rewrite;
+
+import com.google.gwt.dev.asm.ClassAdapter;
+import com.google.gwt.dev.asm.ClassVisitor;
+import com.google.gwt.dev.asm.MethodAdapter;
+import com.google.gwt.dev.asm.MethodVisitor;
+import com.google.gwt.dev.asm.Opcodes;
+import com.google.gwt.dev.asm.commons.Remapper;
+import com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter.InstanceMethodOracle;
+
+import java.util.Set;
+
+/**
+ * Rewrites references to modified JSO subtypes.
+ * 
+ * <ol>
+ * <li>Changes the owner type for instructions that reference items in a JSO
+ * class to the implementation class.</li>
+ * <li>Rewrites instance calls to JSO classes into static calls.</li>
+ * <li>Updates the descriptor for such call sites to includes a synthetic
+ * <code>this</code> parameter. This modified method has same stack behavior
+ * as the original instance method.</li>
+ * </ol>
+ */
+class RewriteRefsToJsoClasses extends ClassAdapter {
+
+  /**
+   * A method body rewriter to actually rewrite call sites.
+   */
+  private class MyMethodAdapter extends MethodAdapter {
+
+    private Remapper remapper = new Remapper() {
+      @Override
+      public String map(String typeName) {
+        if (jsoDescriptors.contains(typeName)) {
+          return HostedModeClassRewriter.JAVASCRIPTOBJECT_IMPL_DESC;
+        }
+        return typeName;
+      }
+    };
+
+    public MyMethodAdapter(MethodVisitor mv) {
+      super(mv);
+    }
+
+    @Override
+    public void visitFieldInsn(int opcode, String owner, String name,
+        String desc) {
+      if (jsoDescriptors.contains(owner)) {
+        // Change the owner to the rewritten class.
+        owner += "$";
+      }
+      super.visitFieldInsn(opcode, owner, name, desc);
+    }
+
+    @Override
+    public void visitLdcInsn(Object cst) {
+      cst = remapper.mapValue(cst);
+      super.visitLdcInsn(cst);
+    }
+
+    @Override
+    public void visitMethodInsn(int opcode, String owner, String name,
+        String desc) {
+      if (jsoDescriptors.contains(owner)) {
+        // Find the class that actually declared the method.
+        if (opcode == Opcodes.INVOKEVIRTUAL) {
+          owner = mapper.findOriginalDeclaringClass(owner, name + desc);
+        }
+        if (!owner.equals("java/lang/Object")) {
+          if (opcode == Opcodes.INVOKEVIRTUAL
+              || opcode == Opcodes.INVOKESPECIAL) {
+            // Instance/super call to JSO; rewrite as static.
+            opcode = Opcodes.INVOKESTATIC;
+            desc = HostedModeClassRewriter.addSyntheticThisParam(owner, desc);
+            name += "$";
+          }
+          // Change the owner to implementation class.
+          owner += "$";
+        }
+      }
+      super.visitMethodInsn(opcode, owner, name, desc);
+    }
+
+    @Override
+    public void visitMultiANewArrayInsn(String desc, int dims) {
+      desc = remapper.mapType(desc);
+      super.visitMultiANewArrayInsn(desc, dims);
+    }
+
+    @Override
+    public void visitTypeInsn(int opcode, String type) {
+      if (opcode == Opcodes.ANEWARRAY) {
+        type = remapper.mapType(type);
+      }
+      super.visitTypeInsn(opcode, type);
+    }
+  }
+
+  /**
+   * An unmodifiable set of descriptors containing <code>JavaScriptObject</code>
+   * and all subclasses.
+   */
+  protected final Set<String> jsoDescriptors;
+
+  /**
+   * Maps methods to the class in which they are declared.
+   */
+  private InstanceMethodOracle mapper;
+
+  /**
+   * Construct a new rewriter instance.
+   * 
+   * @param cv the visitor to chain to
+   * @param jsoDescriptors an unmodifiable set of descriptors containing
+   *          <code>JavaScriptObject</code> and all subclasses
+   * @param mapper maps methods to the class in which they are declared
+   */
+  public RewriteRefsToJsoClasses(ClassVisitor cv, Set<String> jsoDescriptors,
+      InstanceMethodOracle mapper) {
+    super(cv);
+    this.jsoDescriptors = jsoDescriptors;
+    this.mapper = mapper;
+  }
+
+  @Override
+  public MethodVisitor visitMethod(int access, String name, String desc,
+      String signature, String[] exceptions) {
+    // Wrap the returned method visitor in my own.
+    MethodVisitor mv = super.visitMethod(access, name, desc, signature,
+        exceptions);
+    return new MyMethodAdapter(mv);
+  }
+
+}
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
new file mode 100644
index 0000000..b6c7c1e
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/shell/rewrite/RewriteSingleJsoImplDispatches.java
@@ -0,0 +1,349 @@
+/*
+ * 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.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;
+import com.google.gwt.dev.asm.MethodVisitor;
+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.shell.rewrite.HostedModeClassRewriter.SingleJsoImplData;
+import com.google.gwt.dev.util.collect.Maps;
+import com.google.gwt.dev.util.collect.Sets;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+/**
+ * Effects the renaming of {@code @SingleJsoImpl} methods from their original
+ * name to their mangled name. Let us call the original method an "unmangled
+ * method" and the new method a "mangled method". There are three steps in this
+ * process:
+ * <ol>
+ * <li>Within {@code @SingleJsoImpl} interfaces rename all unmangled methods to
+ * become mangled methods.</li>
+ * <li>Within non-JSO classes containing a concrete implementation of an
+ * unmangled method, add a mangled method which is implemented as a simple
+ * trampoline to the unmangled method. (We don't do this in JSO classes here
+ * because the one-and-only trampoline lives in JavaScriptObject$ and is emitted
+ * in {@link WriteJsoImpl}).
+ * <li>Update all call sites targeting unmangled methods to target mangled
+ * methods instead, provided the caller is binding to the interface rather than
+ * a concrete type.</li>
+ * </ol>
+ */
+public class RewriteSingleJsoImplDispatches extends ClassAdapter {
+  private class MyMethodVisitor extends MethodAdapter {
+    public MyMethodVisitor(MethodVisitor mv) {
+      super(mv);
+    }
+
+    /*
+     * Implements objective #3: updates call sites to unmangled methods.
+     */
+    @Override
+    public void visitMethodInsn(int opcode, String owner, String name,
+        String desc) {
+      if (opcode == Opcodes.INVOKEINTERFACE) {
+        if (jsoData.getSingleJsoIntfTypes().contains(owner)) {
+          // Simple case; referring directly to a SingleJso interface.
+          name = owner.replace('/', '_') + "_" + name;
+          assert jsoData.getMangledNames().contains(name) : "Missing " + name;
+
+        } else {
+          /*
+           * Might be referring to a subtype of a SingleJso interface:
+           * 
+           * interface IA { void foo() }
+           * 
+           * interface JA extends JSO implements IA;
+           * 
+           * interface IB extends IA {}
+           * 
+           * void bar() { ((IB) object).foo(); }
+           */
+          outer : for (String intf : computeAllInterfaces(owner)) {
+            if (jsoData.getSingleJsoIntfTypes().contains(intf)) {
+              /*
+               * Check that it really should be mangled and is not a reference
+               * to a method defined in a non-singleJso super-interface. If
+               * there are two super-interfaces that define methods with
+               * identical names and descriptors, the choice of implementation
+               * is undefined.
+               */
+              String maybeMangled = intf.replace('/', '_') + "_" + name;
+              List<Method> methods = jsoData.getImplementations(maybeMangled);
+              if (methods != null) {
+                for (Method method : methods) {
+                  /*
+                   * Found a method with the right name, but we need to check
+                   * the parameters and the return type. In order to do this,
+                   * we'll look at the arguments and return type of the target
+                   * method, removing the first argument, which is the instance.
+                   */
+                  assert method.getArgumentTypes().length >= 1;
+                  Type[] argumentTypes = new Type[method.getArgumentTypes().length - 1];
+                  System.arraycopy(method.getArgumentTypes(), 1, argumentTypes,
+                      0, argumentTypes.length);
+                  String maybeDescriptor = Type.getMethodDescriptor(
+                      method.getReturnType(), argumentTypes);
+                  if (maybeDescriptor.equals(desc)) {
+                    name = maybeMangled;
+                    break outer;
+                  }
+                }
+              }
+            }
+          }
+        }
+      }
+
+      super.visitMethodInsn(opcode, owner, name, desc);
+    }
+  }
+
+  private String currentTypeName;
+  private final Set<String> implementedMethods = new HashSet<String>();
+  private boolean inSingleJsoImplInterfaceType;
+  private Map<String, Set<String>> intfNamesToAllInterfaces = Maps.create();
+  private final SingleJsoImplData jsoData;
+  private final TypeOracle typeOracle;
+
+  public RewriteSingleJsoImplDispatches(ClassVisitor v, TypeOracle typeOracle,
+      SingleJsoImplData jsoData) {
+    super(v);
+    this.typeOracle = typeOracle;
+    this.jsoData = jsoData;
+  }
+
+  @Override
+  public void visit(int version, int access, String name, String signature,
+      String superName, String[] interfaces) {
+    assert currentTypeName == null;
+    super.visit(version, access, name, signature, superName, interfaces);
+
+    /*
+     * This visitor would mangle JSO$ since it acts as a roll-up of all
+     * SingleJso types and the result would be repeated method definitions due
+     * to the trampoline methods this visitor would create.
+     */
+    if (name.equals(HostedModeClassRewriter.JAVASCRIPTOBJECT_IMPL_DESC)) {
+      return;
+    }
+
+    currentTypeName = name;
+    inSingleJsoImplInterfaceType = jsoData.getSingleJsoIntfTypes().contains(
+        name);
+
+    /*
+     * Implements objective #2: non-JSO types that implement a SingleJsoImpl
+     * interface don't have their original instance methods altered. Instead, we
+     * add trampoline methods with mangled names that simply call over to the
+     * original methods.
+     */
+    if (interfaces != null && (access & Opcodes.ACC_INTERFACE) == 0) {
+      Set<String> toStub = computeAllInterfaces(interfaces);
+      toStub.retainAll(jsoData.getSingleJsoIntfTypes());
+
+      for (String stubIntr : toStub) {
+        writeTrampoline(stubIntr);
+      }
+    }
+  }
+
+  @Override
+  public void visitEnd() {
+    /*
+     * Add any missing methods that are defined by a super-interface, but that
+     * may be referenced via a more specific interface.
+     */
+    if (inSingleJsoImplInterfaceType) {
+      for (Map.Entry<String, List<Method>> entry : toImplement(currentTypeName).entrySet()) {
+        for (Method method : entry.getValue()) {
+          writeEmptyMethod(entry.getKey(), method);
+        }
+      }
+    }
+    super.visitEnd();
+  }
+
+  @Override
+  public MethodVisitor visitMethod(int access, String name, String desc,
+      String signature, String[] exceptions) {
+
+    /*
+     * Implements objective #2: Rename unmangled methods in a @SingleJsoImpl
+     * into mangled methods (except for clinit, LOL).
+     */
+    if (inSingleJsoImplInterfaceType && !"<clinit>".equals(name)) {
+      name = currentTypeName.replace('/', '_') + "_" + name;
+      implementedMethods.add(name);
+    }
+
+    MethodVisitor mv = super.visitMethod(access, name, desc, signature,
+        exceptions);
+    if (mv == null) {
+      return null;
+    }
+
+    return new MyMethodVisitor(mv);
+  }
+
+  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()) {
+      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.
+   */
+  private SortedMap<String, List<Method>> toImplement(String typeName) {
+    String name = typeName.replace('/', '_');
+    String prefix = name + "_";
+    String suffix = name + "`";
+    SortedMap<String, List<Method>> toReturn = new TreeMap<String, List<Method>>();
+
+    for (String mangledName : jsoData.getMangledNames().subSet(prefix, suffix)) {
+      toReturn.put(mangledName, jsoData.getImplementations(mangledName));
+    }
+    toReturn.keySet().removeAll(implementedMethods);
+    return toReturn;
+  }
+
+  private void writeEmptyMethod(String mangledMethodName, Method method) {
+    assert method.getArgumentTypes().length > 0;
+    // Remove the first argument, which would be the implementing JSO type
+    String descriptor = "("
+        + method.getDescriptor().substring(
+            1 + method.getArgumentTypes()[0].getDescriptor().length());
+
+    // Create the stub method entry in the interface
+    MethodVisitor mv = super.visitMethod(Opcodes.ACC_PUBLIC
+        | Opcodes.ACC_ABSTRACT, mangledMethodName, descriptor, null, null);
+    mv.visitEnd();
+  }
+
+  /**
+   * For regular Java objects that implement a SingleJsoImpl interface, write
+   * instance trampoline dispatchers for mangled method names to the
+   * implementing method.
+   */
+  private void writeTrampoline(String stubIntr) {
+    /*
+     * This is almost the same kind of trampoline as the ones generated in
+     * WriteJsoImpl, however there are enough small differences between the
+     * semantics of the dispatches that would make a common implementation far
+     * more awkward than the duplication of code.
+     */
+    for (Map.Entry<String, List<Method>> entry : toImplement(stubIntr).entrySet()) {
+      for (Method method : entry.getValue()) {
+        String mangledName = entry.getKey();
+
+        String descriptor = "("
+            + method.getDescriptor().substring(
+                1 + method.getArgumentTypes()[0].getDescriptor().length());
+        String localName = method.getName().substring(0,
+            method.getName().length() - 1);
+        Method toCall = new Method(localName, descriptor);
+
+        // Must not be final
+        MethodVisitor mv = super.visitMethod(Opcodes.ACC_PUBLIC
+            | Opcodes.ACC_SYNTHETIC, mangledName, descriptor, null, null);
+        if (mv != null) {
+          mv.visitCode();
+
+          /*
+           * It just so happens that the stack and local variable sizes are the
+           * same, but they're kept distinct to aid in clarity should the
+           * dispatch logic change.
+           * 
+           * These start at 1 because we need to load "this" onto the stack
+           */
+          int var = 1;
+          int size = 1;
+
+          // load this
+          mv.visitVarInsn(Opcodes.ALOAD, 0);
+
+          // then the rest of the arguments
+          for (Type t : toCall.getArgumentTypes()) {
+            size += t.getSize();
+            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));
+          mv.visitMaxs(size, var);
+          mv.visitEnd();
+        }
+      }
+    }
+  }
+}
diff --git a/dev/core/src/com/google/gwt/dev/shell/rewrite/SingleJsoImplSupport.java b/dev/core/src/com/google/gwt/dev/shell/rewrite/SingleJsoImplSupport.java
deleted file mode 100644
index 36a182e..0000000
--- a/dev/core/src/com/google/gwt/dev/shell/rewrite/SingleJsoImplSupport.java
+++ /dev/null
@@ -1,270 +0,0 @@
-/*
- * Copyright 2010 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.dev.shell.rewrite;
-
-import static com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter.CANONICAL_FIELD;
-import static com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter.REWRAP_METHOD;
-import static com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter.SINGLE_JSO_IMPL_ADJUNCT_SUFFIX;
-import static com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter.SINGLE_JSO_IMPL_FIELD;
-
-import com.google.gwt.dev.shell.JsValueGlue;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-
-/**
- * Utility methods that are called by code generated by
- * {@link WriteSingleJsoSupportCode}.
- */
-public class SingleJsoImplSupport {
-
-  /**
-   * Stash a reference to the JavaScriptObject Class object.
-   */
-  private static final ThreadLocal<Class<?>> JSO_CLASS_OBJECT = new ThreadLocal<Class<?>>();
-
-  /**
-   * Called from JsValueGlue to ensure that a JSO instance can be cast to the
-   * desired type. If the desired type is a regular JSO subclass, this is a
-   * simple rewrap call. Otherwise, assume that it's a SingleJsoImpl interface
-   * type and use {@link #cast(Object, Class, Class)}.
-   */
-  public static Object cast(Object o, Class<?> jsoOrIntfType) {
-    assert o != null : "o is null";
-    assert jsoOrIntfType != Object.class : "Not expecting Object";
-
-    if (jsoOrIntfType.isInterface()) {
-      // Casting to an interface type
-      Class<?> targetJsoType = getTargetJsoType(jsoOrIntfType);
-
-      checkTarget(jsoOrIntfType, targetJsoType);
-
-      // Let the no-implementing-type code in cast(3) handle the error
-      return rewrap(targetJsoType, o);
-    }
-
-    if (getJsoClass(o).isAssignableFrom(jsoOrIntfType)) {
-      // A JSO type
-      return rewrap(jsoOrIntfType, o);
-    }
-
-    // Something else, let the call-site hit CCE
-    return o;
-  }
-
-  /**
-   * Called by synthetic code to cast an object <code>o</code> to some concrete
-   * type that implements the interface <code>intfType</code>. If a
-   * JavaScriptObject subtype has been loaded that implements the interface,
-   * that type will be provided in <code>targetJsoType</code>.
-   * 
-   * @return an object that implements <code>intfType</code>
-   * @throws ClassCastException if <code>o</code> is a JSO subtype and there is
-   *           no SingleJsoImpl type for <code>intfType</code>
-   */
-  public static Object cast(Object o, Class<?> intfType, Class<?> targetJsoType) {
-    if (o == null || intfType.isInstance(o) || !getJsoClass(o).isInstance(o)) {
-      // Let class cast exception happen at callsite
-      return o;
-    }
-
-    checkTarget(intfType, targetJsoType);
-
-    return rewrap(targetJsoType, o);
-  }
-
-  /**
-   * Called by synthetic code when the object on the stack might be a JSO
-   * wrapper and we want the canonical object.
-   */
-  public static Object ensureCanonical(Object o) {
-    if (o == null) {
-      return null;
-    }
-
-    Class<?> jsoClass = getJsoClass(o);
-    if (jsoClass.isInstance(o)) {
-      Exception ex;
-      try {
-        return jsoClass.getField(CANONICAL_FIELD).get(o);
-      } catch (IllegalArgumentException e) {
-        ex = e;
-      } catch (SecurityException e) {
-        ex = e;
-      } catch (IllegalAccessException e) {
-        ex = e;
-      } catch (NoSuchFieldException e) {
-        ex = e;
-      }
-      throw new RuntimeException("Unable to determine canonical object", ex);
-    }
-
-    return o;
-  }
-
-  /**
-   * Returns the JSO implementation type declared on the interface via a
-   * SingleJsoImpl annotation.
-   */
-  @SuppressWarnings("unchecked")
-  public static Class<?> getDeclaredSingleJsoImplType(Class<?> intfType) {
-    Exception ex;
-    try {
-      Class singleJsoImplType = intfType.getClassLoader().loadClass(
-          "com.google.gwt.core.client.SingleJsoImpl");
-      Object annotation = intfType.getAnnotation(singleJsoImplType);
-      if (annotation != null) {
-        Method m = singleJsoImplType.getMethod("value");
-        Class<?> value = (Class<?>) m.invoke(annotation);
-        return value;
-      }
-
-      // Try the by-name annotation
-      singleJsoImplType = intfType.getClassLoader().loadClass(
-          "com.google.gwt.core.client.SingleJsoImplName");
-      annotation = intfType.getAnnotation(singleJsoImplType);
-      if (annotation != null) {
-        Method m = singleJsoImplType.getMethod("value");
-        String name = (String) m.invoke(annotation);
-
-        return Class.forName(name, true, intfType.getClassLoader());
-      }
-
-      return null;
-    } catch (ClassNotFoundException e) {
-      ex = e;
-    } catch (IllegalArgumentException e) {
-      ex = e;
-    } catch (IllegalAccessException e) {
-      ex = e;
-    } catch (InvocationTargetException e) {
-      ex = e;
-    } catch (SecurityException e) {
-      ex = e;
-    } catch (NoSuchMethodException e) {
-      ex = e;
-    }
-    throw new RuntimeException("Unable to determine SingleJsoImpl type", ex);
-  }
-
-  /**
-   * Called by synthetic code to implement instanceof tests.
-   * 
-   * @param o the object to test
-   * @param intf the interface type that <code>o</code> is being tested against
-   * @param implJsoType the JavaScriptObject subtype that implements
-   *          <code>intf</code> if it has been loaded
-   * @return <code>true</code> if <code>o</code> implements <code>intf</code> or
-   *         if <code>o</code> is a JSO and some JSO type that implements
-   *         <code>intf</code> has been loaded
-   */
-  public static boolean instanceOf(Object o, Class<?> intf, Class<?> implJsoType) {
-    return intf.isInstance(o)
-        || (implJsoType != null && getJsoClass(o).isInstance(o));
-  }
-
-  /**
-   * Throw a specialized ClassCastException if <code>targetJsoType</code> is
-   * null.
-   */
-  private static void checkTarget(Class<?> intfType, Class<?> targetJsoType) {
-    if (targetJsoType == null) {
-      throw new ClassCastException(
-          "There is no known JavaScriptObject subtype that implements "
-              + intfType.getCanonicalName()
-              + ". Fix by adding an @SingleJsoImpl annotation to "
-              + intfType.getSimpleName()
-              + " or by referencing the concrete JSO type.");
-    }
-  }
-
-  /**
-   * Load the JavaScriptObject Class object from a client object's isolated
-   * ClassLoader.
-   */
-  private static Class<?> getJsoClass(Object jso) {
-    Class<?> toReturn = JSO_CLASS_OBJECT.get();
-    if (toReturn != null) {
-      return toReturn;
-    }
-    try {
-      toReturn = jso.getClass().getClassLoader().loadClass(
-          JsValueGlue.JSO_CLASS);
-    } catch (ClassNotFoundException e) {
-      throw new RuntimeException("No JavaScriptObject class", e);
-    }
-    JSO_CLASS_OBJECT.set(toReturn);
-    return toReturn;
-  }
-
-  private static Class<?> getTargetJsoType(Class<?> intfType) {
-    Field f;
-    Exception ex;
-    try {
-      intfType = intfType.getClassLoader().loadClass(
-          intfType.getName() + SINGLE_JSO_IMPL_ADJUNCT_SUFFIX);
-      f = intfType.getField(SINGLE_JSO_IMPL_FIELD);
-      return (Class<?>) f.get(null);
-    } catch (SecurityException e) {
-      ex = e;
-    } catch (NoSuchFieldException e) {
-      ex = e;
-    } catch (IllegalArgumentException e) {
-      ex = e;
-    } catch (IllegalAccessException e) {
-      ex = e;
-    } catch (ClassNotFoundException e) {
-      ex = e;
-    }
-    throw new RuntimeException("Unable to query singleJsoImpl type", ex);
-  }
-
-  /**
-   * Reflectively invoke a JSO subtype's rewrap method to create a new wrapper
-   * instance for a JavaScriptObject.
-   * 
-   * @param jsoClass the desired wrapper type
-   * @param jso a JavaScriptObject
-   * @return a JavaScriptObject wrapper that encloses <code>jso</code>'s
-   *         canonical JSO identity
-   */
-  private static Object rewrap(Class<?> jsoClass, Object jso) {
-    Method m;
-    Exception ex;
-    try {
-      m = jsoClass.getMethod(REWRAP_METHOD, getJsoClass(jsoClass));
-      return m.invoke(null, jso);
-    } catch (NoSuchMethodException e) {
-      throw new RuntimeException("Missing expected synthetic method", e);
-    } catch (SecurityException e) {
-      ex = e;
-    } catch (IllegalArgumentException e) {
-      ex = e;
-    } catch (IllegalAccessException e) {
-      ex = e;
-    } catch (InvocationTargetException e) {
-      ex = e;
-    }
-    throw new RuntimeException(ex);
-  }
-
-  /**
-   * Utility class.
-   */
-  private SingleJsoImplSupport() {
-  }
-}
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 92acc1b..4eafd89 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
@@ -15,79 +15,94 @@
  */
 package com.google.gwt.dev.shell.rewrite;
 
-import static com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter.CANONICAL_FIELD;
-import static com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter.JAVASCRIPTOBJECT_DESC;
-import static com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter.REFERENCE_FIELD;
-import static com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter.REWRAP_METHOD;
-
 import com.google.gwt.dev.asm.ClassAdapter;
 import com.google.gwt.dev.asm.ClassVisitor;
 import com.google.gwt.dev.asm.FieldVisitor;
-import com.google.gwt.dev.asm.Label;
 import com.google.gwt.dev.asm.MethodVisitor;
 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.shell.rewrite.HostedModeClassRewriter.InstanceMethodOracle;
+import com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter.SingleJsoImplData;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
 
 /**
  * Writes the implementation classes for JSO and its subtypes.
+ * 
+ * Changes made by the base class:
+ * <ol>
+ * <li>The new type has the same name as the old type with a '$' appended.</li>
+ * <li>All instance methods in the original type become static methods taking an
+ * explicit <code>this</code> parameter. Such methods have the same stack
+ * behavior as the original.</li>
+ * </ol>
  */
-class WriteJsoImpl {
+abstract class WriteJsoImpl extends ClassAdapter {
 
   /**
    * This type implements JavaScriptObject.
    * 
-   * <ul>
+   * <ol>
    * <li>JavaScriptObject itself gets a new synthetic field to store the
    * underlying hosted mode reference.</li>
-   * <li>It also receives a field to retain the canonical JavaScriptObject when
-   * creating wrapper subclasses.</li>
-   * <li>A rewrap method is added that simply returns the input object's
-   * canonical object.</li>
-   * <li>The zero-arg constructor is made public and makes the JavaScriptObject
-   * its own canonical object.</li>
-   * <li>A one-arg constructor is added for use by subclasses that copies the
-   * hosted mode reference and canonical identity object.</li>
-   * </ul>
+   * <li>Instance methods are added so that JavaScriptObject implements all
+   * SingleJsoImpl interfaces.</li>
+   * </ol>
    * 
    */
-  private static class ForJso extends ClassAdapter {
-    public ForJso(ClassVisitor cv) {
-      super(cv);
+  private static class ForJsoDollar extends WriteJsoImpl {
+    /**
+     * An unmodifiable set of descriptors containing
+     * <code>JavaScriptObject</code> and all subclasses.
+     */
+    private final Set<String> jsoDescriptors;
+    private final SingleJsoImplData jsoData;
+
+    public ForJsoDollar(ClassVisitor cv, Set<String> jsoDescriptors,
+        InstanceMethodOracle mapper, SingleJsoImplData jsoData) {
+      super(cv, mapper);
+      this.jsoDescriptors = jsoDescriptors;
+      this.jsoData = jsoData;
     }
 
     @Override
     public void visit(int version, int access, String name, String signature,
         String superName, String[] interfaces) {
 
-      super.visit(version, Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC
-          | Opcodes.ACC_SYNTHETIC, name, signature, superName, interfaces);
+      ArrayList<String> jsoDescList = new ArrayList<String>();
+      jsoDescList.addAll(jsoDescriptors);
+      interfaces = jsoDescList.toArray(new String[jsoDescList.size()]);
 
-      // Generate JavaScriptObject.rewrap$()
-      MethodVisitor mv = super.visitMethod(Opcodes.ACC_PUBLIC
-          | Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC, REWRAP_METHOD, "(L"
-          + JAVASCRIPTOBJECT_DESC + ";)L" + name + ";", null, null);
-      if (mv != null) {
-        writeRewrapMethod(mv);
-      }
+      super.visit(version, access, name, signature, superName, interfaces);
 
       /*
        * Generate the synthetic "hostedModeReferece" field to contain the
        * underlying real reference to the JavaScript object.
        */
-      FieldVisitor fv = visitField(Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC
-          | Opcodes.ACC_FINAL, REFERENCE_FIELD, "Ljava/lang/Object;", null,
+      FieldVisitor fv = visitField(Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC,
+          HostedModeClassRewriter.REFERENCE_FIELD, "Ljava/lang/Object;", null,
           null);
       if (fv != null) {
         fv.visitEnd();
       }
 
-      /*
-       * Generate a synthetic "canonical" field.
-       */
-      fv = visitField(Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC
-          | Opcodes.ACC_FINAL, CANONICAL_FIELD, "L" + JAVASCRIPTOBJECT_DESC
-          + ";", null, null);
-      if (fv != null) {
-        fv.visitEnd();
+      // Implement the trampoline methods
+      for (String mangledName : jsoData.getMangledNames()) {
+        List<Method> declarations = jsoData.getDeclarations(mangledName);
+        List<Method> implementations = jsoData.getImplementations(mangledName);
+        assert declarations.size() == implementations.size() : "Declaration / implementation size mismatch";
+
+        Iterator<Method> declIterator = declarations.iterator();
+        Iterator<Method> implIterator = implementations.iterator();
+
+        while (declIterator.hasNext()) {
+          assert implIterator.hasNext();
+          writeTrampoline(mangledName, declIterator.next(), implIterator.next());
+        }
       }
     }
 
@@ -95,364 +110,123 @@
     public MethodVisitor visitMethod(int access, String name, String desc,
         String signature, String[] exceptions) {
       if (isCtor(name)) {
-        writeConstructors(name);
-        return null;
-      } else if ("equals".equals(name) && "(Ljava/lang/Object;)Z".equals(desc)) {
-        writeEquals(access, name, desc, signature, exceptions);
-        return null;
+        // make the JavaScriptObject$ constructor public
+        access &= ~(Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED);
+        access |= Opcodes.ACC_PUBLIC;
       }
       return super.visitMethod(access, name, desc, signature, exceptions);
     }
 
     /**
-     * Generates a method to return the canonical object.
+     * JSO methods are implemented as flyweight style, with the instance being
+     * passed as the first parameter. This loop create instance methods on JSO$
+     * for all of the mangled SingleJsoImpl interface method names. These
+     * instance methods simply turn around and call the static-dispatch methods.
+     * In Java, it might look like:
      * 
      * <pre>
-     * public JavaScriptObject rewrap$(JavaScriptObject o) {
-     *   if (o == null) {
-     *     return null;
+     * interface Interface {
+     *   String someMethod(int a, double b);
+     * }
+     * 
+     * class J extends JSO implements I {
+     *   public String com_google_Interface_someMethod(int a, double b) {
+     *     return com.google.MyJso$.someMethod$(this, a, b);
      *   }
-     *   return o.canonical;
      * }
      * </pre>
-     */
-    protected void writeRewrapMethod(MethodVisitor mv) {
-      Label start = new Label();
-      Label end = new Label();
-
-      mv.visitCode();
-      mv.visitLabel(start);
-
-      mv.visitVarInsn(Opcodes.ALOAD, 0);
-      // Stack is: jso
-      mv.visitInsn(Opcodes.DUP);
-      // Stack is: jso, jso
-
-      Label ret = new Label();
-      mv.visitJumpInsn(Opcodes.IFNULL, ret);
-      // Stack is: jso
-
-      mv.visitFieldInsn(Opcodes.GETFIELD, JAVASCRIPTOBJECT_DESC,
-          CANONICAL_FIELD, "L" + JAVASCRIPTOBJECT_DESC + ";");
-      // Stack is: canonical
-
-      mv.visitLabel(ret);
-      mv.visitFrame(Opcodes.F_NEW, 1, new Object[] {JAVASCRIPTOBJECT_DESC}, 1,
-          new Object[] {JAVASCRIPTOBJECT_DESC});
-      mv.visitInsn(Opcodes.ARETURN);
-
-      mv.visitMaxs(2, 1);
-      mv.visitLabel(end);
-      mv.visitLocalVariable("jso", "L" + JAVASCRIPTOBJECT_DESC + ";", null,
-          start, end, 0);
-      mv.visitEnd();
-    }
-
-    /**
-     * Write JavaScriptObject's constructors.
      * 
-     * <pre>
-     * public JavaScriptObject(Object hostedModeReference) {
-     *   this.canonical = this;
-     *   this.hostedModeReference = hostedModeReference;
-     * }
-     * protected JavaScriptObject(JavaScriptObject jso) {
-     *   this.canonical = jso.canonical;
-     *   this.hostedModeReference = jso.hostedModeReference;
-     * }
-     * </pre>
+     * @param mangledName {@code com_google_gwt_sample_hello_client_Interface_a}
+     * @param interfaceMethod {@code java.lang.String a(int, double)}
+     * @param implementingMethod {@code static final java.lang.String
+     *          a$(com.google.gwt.sample.hello.client.Jso, ...);}
      */
-    private void writeConstructors(String name) {
-      // Write the zero-arg constructor
-      MethodVisitor mv = super.visitMethod(Opcodes.ACC_PUBLIC
-          | Opcodes.ACC_SYNTHETIC, name, "(Ljava/lang/Object;)V", null, null);
+    private void writeTrampoline(String mangledName, Method interfaceMethod,
+        Method implementingMethod) {
+      assert implementingMethod.getArgumentTypes().length > 0;
+
+      /*
+       * The local descriptor is the same as the descriptor from the abstract
+       * method in the interface.
+       */
+      String localDescriptor = interfaceMethod.getDescriptor();
+      Method localMethod = new Method(mangledName, localDescriptor);
+
+      /*
+       * We also use the first argument to know which type to statically
+       * dispatch to.
+       */
+      Type implementingType = Type.getType("L"
+          + implementingMethod.getArgumentTypes()[0].getInternalName() + "$;");
+
+      // Maybe create the method. This is marked final as a sanity check
+      MethodVisitor mv = visitMethodNoRewrite(Opcodes.ACC_PUBLIC
+          | Opcodes.ACC_FINAL | Opcodes.ACC_SYNTHETIC, localMethod.getName(),
+          localMethod.getDescriptor(), null, null);
+
       if (mv != null) {
         mv.visitCode();
-        // Call Object's constructor
-        mv.visitVarInsn(Opcodes.ALOAD, 0);
-        mv.visitInsn(Opcodes.DUP);
-        // Stack: this, this
-        mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>",
-            "()V");
-        // Stack: this
 
-        // this.canonical = this;
-        mv.visitInsn(Opcodes.DUP);
-        mv.visitInsn(Opcodes.DUP);
-        mv.visitVarInsn(Opcodes.ALOAD, 1);
-        // Stack: this, this, this, hostedModeReference
-        mv.visitFieldInsn(Opcodes.PUTFIELD, JAVASCRIPTOBJECT_DESC,
-            REFERENCE_FIELD, "Ljava/lang/Object;");
-        // Stack: this, this
-        mv.visitFieldInsn(Opcodes.PUTFIELD, JAVASCRIPTOBJECT_DESC,
-            CANONICAL_FIELD, "L" + JAVASCRIPTOBJECT_DESC + ";");
-        // Stack: <empty>
+        /*
+         * It just so happens that the stack and local variable sizes are the
+         * same, but they're kept distinct to aid in clarity should the dispatch
+         * logic change.
+         */
+        int var = 0;
+        int size = 0;
 
-        mv.visitInsn(Opcodes.RETURN);
-        mv.visitMaxs(4, 2);
+        for (Type t : implementingMethod.getArgumentTypes()) {
+          size += t.getSize();
+          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());
+        mv.visitInsn(localMethod.getReturnType().getOpcode(Opcodes.IRETURN));
+        mv.visitMaxs(size, var);
         mv.visitEnd();
       }
-
-      // Write the protected one-arg constructor
-      mv = super.visitMethod(Opcodes.ACC_PROTECTED | Opcodes.ACC_SYNTHETIC,
-          name, "(L" + JAVASCRIPTOBJECT_DESC + ";)V", null, null);
-      if (mv != null) {
-        Label start = new Label();
-        Label end = new Label();
-
-        // Call Object's constructor
-        mv.visitCode();
-        mv.visitLabel(start);
-        mv.visitVarInsn(Opcodes.ALOAD, 0);
-        // Stack: this
-        mv.visitInsn(Opcodes.DUP);
-        // Stack: this, this
-        mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>",
-            "()V");
-        // Stack: this
-
-        // this.canonical = otherJso;
-        mv.visitVarInsn(Opcodes.ALOAD, 1);
-        // Stack: this, otherJso
-        mv.visitInsn(Opcodes.DUP2);
-        // Stack: this, otherJso, this, otherJso
-        mv.visitFieldInsn(Opcodes.GETFIELD, JAVASCRIPTOBJECT_DESC,
-            CANONICAL_FIELD, "L" + JAVASCRIPTOBJECT_DESC + ";");
-        // Stack: this, otherJso, this, canonical
-        mv.visitFieldInsn(Opcodes.PUTFIELD, JAVASCRIPTOBJECT_DESC,
-            CANONICAL_FIELD, "L" + JAVASCRIPTOBJECT_DESC + ";");
-        // Stack: this, otherJso
-
-        // this.hostedModeReference = otherJso.hostedModeReference
-        mv.visitFieldInsn(Opcodes.GETFIELD, JAVASCRIPTOBJECT_DESC,
-            REFERENCE_FIELD, "Ljava/lang/Object;");
-        // Stack: this, hostedModeReference
-        mv.visitFieldInsn(Opcodes.PUTFIELD, JAVASCRIPTOBJECT_DESC,
-            REFERENCE_FIELD, "Ljava/lang/Object;");
-        // Stack: <empty>
-
-        mv.visitInsn(Opcodes.RETURN);
-        mv.visitMaxs(4, 2);
-        mv.visitLabel(end);
-        mv.visitLocalVariable("this", "L" + JAVASCRIPTOBJECT_DESC + ";", null,
-            start, end, 0);
-        mv.visitLocalVariable("jso", "L" + JAVASCRIPTOBJECT_DESC + ";", null,
-            start, end, 1);
-        mv.visitEnd();
-      }
-    }
-
-    /**
-     * Write the implementation of JSO.equals() to use a regular object-identity
-     * comparison that can be rewritten further.
-     * 
-     * <pre>
-     * public boolean equals(Object other) {
-     *   return this == other;
-     * }
-     * </pre>
-     */
-    private void writeEquals(int access, String name, String desc,
-        String signature, String[] exceptions) {
-      MethodVisitor mv = super.visitMethod(access, name, desc, signature,
-          exceptions);
-      if (mv == null) {
-        return;
-      }
-      mv.visitCode();
-
-      Label returnTrue = new Label();
-      mv.visitVarInsn(Opcodes.ALOAD, 0);
-      mv.visitVarInsn(Opcodes.ALOAD, 1);
-      // Stack: this, other
-
-      mv.visitJumpInsn(Opcodes.IF_ACMPEQ, returnTrue);
-      mv.visitInsn(Opcodes.ICONST_0);
-      // Stack: 0
-      mv.visitInsn(Opcodes.IRETURN);
-
-      mv.visitLabel(returnTrue);
-      mv.visitFrame(Opcodes.F_NEW, 2, new Object[] {
-          JAVASCRIPTOBJECT_DESC, "java/lang/Object"}, 0, new Object[0]);
-      mv.visitInsn(Opcodes.ICONST_1);
-      // Stack: 1
-      mv.visitInsn(Opcodes.IRETURN);
-
-      mv.visitMaxs(2, 2);
-      mv.visitEnd();
     }
   }
 
   /**
    * This type is used to implement subtypes of JSO.
    * 
-   * <ul>
-   * <li>The type's zero-arg constructor is replaced with a one-arg copy
-   * constructor that delegates to the one-arg super-constructor.</li>
-   * <li>A static rewrap method is added</li>
-   * </ul>
+   * <ol>
+   * <li>The new type's superclass is mangled by adding $.</li>
+   * <li>Constructors are deleted.</li>
+   * </ol>
    */
-  private static class ForJsoSubclass extends ClassAdapter {
-    private String superName;
-    private String typeName;
-
-    public ForJsoSubclass(ClassVisitor cv) {
-      super(cv);
+  private static class ForJsoInterface extends WriteJsoImpl {
+    public ForJsoInterface(ClassVisitor cv, InstanceMethodOracle mapper) {
+      super(cv, mapper);
     }
 
     @Override
     public void visit(int version, int access, String name, String signature,
         String superName, String[] interfaces) {
-      this.superName = superName;
-      this.typeName = name;
-      super.visit(version, Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC
-          | Opcodes.ACC_SYNTHETIC, name, signature, superName, interfaces);
+      // Reference the old superclass's implementation class.
+      superName += '$';
+      interfaces = null;
 
-      // Generate JsoSubtype.rewrap$()
-      MethodVisitor mv = super.visitMethod(Opcodes.ACC_PUBLIC
-          | Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC, REWRAP_METHOD, "(L"
-          + JAVASCRIPTOBJECT_DESC + ";)L" + name + ";", null, null);
-      if (mv != null) {
-        writeRewrapMethod(mv);
-      }
+      super.visit(version, access, name, signature, superName, interfaces);
     }
 
-    /**
-     * Rewrite the JSO's constructor.
-     * 
-     * <pre>
-     * protected SomeJso(JavaScriptObject other) {
-     *   super(other);
-     * }
-     * </pre>
-     */
     @Override
     public MethodVisitor visitMethod(int access, String name, String desc,
         String signature, String[] exceptions) {
       boolean isCtor = isCtor(name);
       if (isCtor) {
-        MethodVisitor mv = super.visitMethod(Opcodes.ACC_PROTECTED
-            | Opcodes.ACC_SYNTHETIC, name,
-            "(L" + JAVASCRIPTOBJECT_DESC + ";)V", null, null);
-        if (mv == null) {
-          return null;
-        }
-
-        Label start = new Label();
-        Label end = new Label();
-        mv.visitCode();
-        mv.visitLabel(start);
-        // super(otherJso)
-        mv.visitVarInsn(Opcodes.ALOAD, 0);
-        // Stack: this
-        mv.visitVarInsn(Opcodes.ALOAD, 1);
-        // Stack: this, other
-        mv.visitMethodInsn(Opcodes.INVOKESPECIAL, superName, "<init>", "(L"
-            + JAVASCRIPTOBJECT_DESC + ";)V");
-        // Stack: <empty>
-        mv.visitInsn(Opcodes.RETURN);
-
-        mv.visitMaxs(2, 2);
-        mv.visitLabel(end);
-
-        // For debugging
-        mv.visitLocalVariable("this", "L" + typeName + ";", null, start, end, 0);
-        mv.visitLocalVariable("jso", "L" + JAVASCRIPTOBJECT_DESC + ";", null,
-            start, end, 1);
-        mv.visitEnd();
-
+        // Don't copy over constructors except for JavaScriptObject itself.
         return null;
       }
       return super.visitMethod(access, name, desc, signature, exceptions);
     }
-
-    /**
-     * Constructs a type-specific rewrap method.
-     * 
-     * <pre>
-     * public static JsoSubclass rewrap$(JavaScriptObject jso) {
-     *   start:
-     *   if (jso == null) {
-     *     topOfStack = null;
-     *     goto doReturn;
-     *   }
-     *   
-     *   notNull: if (jso instanceof JsoSubclass) {
-     *     topOfStack = (JsoSubclass) jso;
-     *     goto doReturn;
-     *   }
-     *   
-     *   notMySubclass: topOfStack = new JsoSubclass(jso);
-     *   
-     *   doReturn: return topOfStack;
-     *   end:
-     * }
-     * </pre>
-     */
-    protected void writeRewrapMethod(MethodVisitor mv) {
-      Label start = new Label();
-      Label notNull = new Label();
-      Label notMySubclass = new Label();
-      Label doReturn = new Label();
-      Label end = new Label();
-
-      mv.visitCode();
-      mv.visitLabel(start);
-      mv.visitVarInsn(Opcodes.ALOAD, 0);
-      mv.visitInsn(Opcodes.DUP);
-      // Stack is: jso, jso
-
-      mv.visitJumpInsn(Opcodes.IFNONNULL, notNull);
-      // Stack is: jso
-      // Push a null instead of using dup so that we don't need a useless cast
-      mv.visitInsn(Opcodes.POP);
-      mv.visitInsn(Opcodes.ACONST_NULL);
-      // Stack is: null
-      mv.visitJumpInsn(Opcodes.GOTO, doReturn);
-
-      mv.visitLabel(notNull);
-      mv.visitFrame(Opcodes.F_NEW, 1, new Object[] {JAVASCRIPTOBJECT_DESC}, 1,
-          new Object[] {JAVASCRIPTOBJECT_DESC});
-      mv.visitInsn(Opcodes.DUP);
-      // Stack is: jso, jso
-      mv.visitTypeInsn(Opcodes.INSTANCEOF, typeName);
-      // Stack is: jso, boolean
-      mv.visitJumpInsn(Opcodes.IFEQ, notMySubclass);
-      // Stack is: jso
-      mv.visitTypeInsn(Opcodes.CHECKCAST, typeName);
-      mv.visitJumpInsn(Opcodes.GOTO, doReturn);
-
-      mv.visitLabel(notMySubclass);
-      mv.visitFrame(Opcodes.F_NEW, 1, new Object[] {JAVASCRIPTOBJECT_DESC}, 1,
-          new Object[] {JAVASCRIPTOBJECT_DESC});
-      // Stack is: jso
-
-      // Allocate the new wrapper instance.
-      mv.visitTypeInsn(Opcodes.NEW, typeName);
-      // Stack is: jso, wrapper
-
-      mv.visitInsn(Opcodes.DUP_X1);
-      // Stack is: wrapper, jso, wrapper
-      mv.visitInsn(Opcodes.SWAP);
-      // Stack is: wrapper, wrapper, jso
-
-      // Invoke the constructor, which will access the canonical object
-      mv.visitMethodInsn(Opcodes.INVOKESPECIAL, typeName, "<init>", "(L"
-          + JAVASCRIPTOBJECT_DESC + ";)V");
-      // Stack is: wrapper
-
-      mv.visitLabel(doReturn);
-      mv.visitFrame(Opcodes.F_NEW, 1, new Object[] {JAVASCRIPTOBJECT_DESC}, 1,
-          new Object[] {JAVASCRIPTOBJECT_DESC});
-      // Stack is: toReturn
-      mv.visitInsn(Opcodes.ARETURN);
-
-      mv.visitLabel(end);
-      mv.visitMaxs(3, 1);
-      mv.visitLocalVariable("jso", "L" + JAVASCRIPTOBJECT_DESC + ";", null,
-          start, end, 0);
-      mv.visitEnd();
-    }
   }
 
   /**
@@ -460,21 +234,89 @@
    * select between a simple implementation for user-defined JSO subtypes and
    * the complex implementation for implementing JavaScriptObject$.
    */
-  public static ClassVisitor create(ClassVisitor cv, String classDescriptor) {
-    if (classDescriptor.equals(JAVASCRIPTOBJECT_DESC)) {
-      return new ForJso(cv);
+  public static ClassVisitor create(ClassVisitor cv, String classDescriptor,
+      Set<String> jsoDescriptors, InstanceMethodOracle mapper,
+      SingleJsoImplData singleJsoImplData) {
+
+    if (classDescriptor.equals(HostedModeClassRewriter.JAVASCRIPTOBJECT_IMPL_DESC)) {
+      return new ForJsoDollar(cv, jsoDescriptors, mapper, singleJsoImplData);
     } else {
-      return new ForJsoSubclass(cv);
+      return new ForJsoInterface(cv, mapper);
     }
   }
 
-  private static boolean isCtor(String name) {
-    return "<init>".equals(name);
+  /**
+   * Maps methods to the class in which they are declared.
+   */
+  private final InstanceMethodOracle mapper;
+
+  /**
+   * The original name of the class being visited.
+   */
+  private String originalName;
+
+  /**
+   * Construct a new rewriter instance.
+   * 
+   * @param cv the visitor to chain to
+   * @param jsoDescriptors an unmodifiable set of descriptors containing
+   *          <code>JavaScriptObject</code> and all subclasses
+   * @param mapper maps methods to the class in which they are declared
+   */
+  private WriteJsoImpl(ClassVisitor cv, InstanceMethodOracle mapper) {
+    super(cv);
+    this.mapper = mapper;
   }
 
   /**
-   * Utility class.
+   * Records the original name and resets access opcodes.
    */
-  private WriteJsoImpl() {
+  @Override
+  public void visit(int version, int access, String name, String signature,
+      String superName, String[] interfaces) {
+    originalName = name;
+    super.visit(version, Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC
+        | Opcodes.ACC_SYNTHETIC, name + '$', signature, superName, interfaces);
+  }
+
+  /**
+   * Mangle all instance methods declared in JavaScriptObject types.
+   */
+  @Override
+  public MethodVisitor visitMethod(int access, String name, String desc,
+      String signature, String[] exceptions) {
+    boolean isCtor = isCtor(name);
+    if (!isCtor && !isStatic(access) && !isObjectMethod(name + desc)) {
+      access |= Opcodes.ACC_STATIC;
+      desc = HostedModeClassRewriter.addSyntheticThisParam(getOriginalName(),
+          desc);
+      name = name + "$";
+    }
+    return super.visitMethod(access, name, desc, signature, exceptions);
+  }
+
+  protected String getOriginalName() {
+    return originalName;
+  }
+
+  protected boolean isCtor(String name) {
+    return "<init>".equals(name);
+  }
+
+  protected boolean isObjectMethod(String signature) {
+    return "java/lang/Object".equals(mapper.findOriginalDeclaringClass(
+        originalName, signature));
+  }
+
+  protected boolean isStatic(int access) {
+    return (access & Opcodes.ACC_STATIC) != 0;
+  }
+
+  /**
+   * Allows access to an unmodified visitMethod call.
+   */
+  protected MethodVisitor visitMethodNoRewrite(int access, String name,
+      String desc, String signature, String[] exceptions) {
+    return super.visitMethod(access, name, desc, signature, exceptions);
   }
 }
diff --git a/dev/core/src/com/google/gwt/dev/shell/rewrite/WriteSingleJsoSupportCode.java b/dev/core/src/com/google/gwt/dev/shell/rewrite/WriteSingleJsoSupportCode.java
deleted file mode 100644
index 05532a3..0000000
--- a/dev/core/src/com/google/gwt/dev/shell/rewrite/WriteSingleJsoSupportCode.java
+++ /dev/null
@@ -1,424 +0,0 @@
-/*
- * Copyright 2010 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.dev.shell.rewrite;
-
-import static com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter.JAVASCRIPTOBJECT_DESC;
-import static com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter.REWRAP_METHOD;
-import static com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter.SINGLE_JSO_IMPL_ADJUNCT_SUFFIX;
-import static com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter.SINGLE_JSO_IMPL_CAST_METHOD;
-import static com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter.SINGLE_JSO_IMPL_CAST_TO_OBJECT_METHOD;
-import static com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter.SINGLE_JSO_IMPL_FIELD;
-import static com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter.SINGLE_JSO_IMPL_INSTANCEOF_METHOD;
-import static com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter.SINGLE_JSO_IMPL_SUPPORT_CLASS;
-import static com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter.SYSTEM_CLASS_VERSION;
-
-import com.google.gwt.dev.asm.ClassAdapter;
-import com.google.gwt.dev.asm.ClassVisitor;
-import com.google.gwt.dev.asm.ClassWriter;
-import com.google.gwt.dev.asm.FieldVisitor;
-import com.google.gwt.dev.asm.Label;
-import com.google.gwt.dev.asm.MethodAdapter;
-import com.google.gwt.dev.asm.MethodVisitor;
-import com.google.gwt.dev.asm.Opcodes;
-import com.google.gwt.dev.asm.Type;
-import com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter.RewriterOracle;
-
-/**
- * Adds code to JavaScriptObject subtypes to register themselves as the
- * implementation type for a given interface. This class also contains a utility
- * method for generating an interface's adjunct type to support SingleJsoImpl
- * dispatch.
- */
-class WriteSingleJsoSupportCode extends ClassAdapter {
-  public static String SINGLE_JSO_IMPL_ASSIGNMENT_METHOD = "assignSingleJso$";
-
-  private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
-
-  /**
-   * Create an adjunct class for every interface type that a JavaScriptObject
-   * might implement. It will contain methods for assisting with casts and
-   * instanceof checks. Given the interface <code>IFoo</code> the following type
-   * will be generated:
-   * 
-   * <pre>
-   * class IFoo$singleJsoImpl {
-   * //Initialized by the JSO subtype
-   * public static Class jsoImplType;
-   * 
-   * static {
-   *   jsoImplType = SingleJsoImplSupport.getDeclaredSingleJsoImplType(IFoo.class)
-   * }
-   * 
-   * public static Object cast(Object o) {
-   *   return SingleJsoImplSupport.cast(o, IFoo.class, jsoImplType);
-   * }
-   *
-   * public static Object castToObject$(InterfaceType o) {
-   *   if (o instanceof JavaScriptObject) {
-   *     o = JavaScriptObject.rewrap$((JavaScriptObject o));
-   *   }
-   *   return o;
-   * }
-   * public static boolean instanceOf(Object o) {
-   *   return SingleJsoImplSupport.instanceOf(o, IFoo.class, jsoImplType);
-   * }
-   * }
-   * </pre>
-   */
-  static byte[] writeSingleJsoImplAdjunct(String className) {
-    assert className.endsWith(SINGLE_JSO_IMPL_ADJUNCT_SUFFIX) : "Bad className "
-        + className;
-    String internalName = toInternalName(className);
-    String intfName = internalName.substring(0, internalName.length()
-        - SINGLE_JSO_IMPL_ADJUNCT_SUFFIX.length());
-
-    ClassWriter writer = new ClassWriter(0);
-
-    writer.visit(SYSTEM_CLASS_VERSION, Opcodes.ACC_PUBLIC
-        | Opcodes.ACC_SYNTHETIC, internalName, null, "java/lang/Object", null);
-
-    // Create jsoImplType field
-    FieldVisitor fv = writer.visitField(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC
-        | Opcodes.ACC_SYNTHETIC, SINGLE_JSO_IMPL_FIELD, "Ljava/lang/Class;",
-        null, null);
-    if (fv != null) {
-      fv.visitEnd();
-    }
-
-    // Write static initializer
-    MethodVisitor mv = writer.visitMethod(Opcodes.ACC_STATIC, "<clinit>",
-        "()V", null, null);
-    if (mv != null) {
-      mv.visitCode();
-      mv.visitLdcInsn(Type.getObjectType(intfName));
-      mv.visitMethodInsn(Opcodes.INVOKESTATIC, SINGLE_JSO_IMPL_SUPPORT_CLASS,
-          "getDeclaredSingleJsoImplType",
-          "(Ljava/lang/Class;)Ljava/lang/Class;");
-      mv.visitFieldInsn(Opcodes.PUTSTATIC, internalName, SINGLE_JSO_IMPL_FIELD,
-          "Ljava/lang/Class;");
-      mv.visitInsn(Opcodes.RETURN);
-      mv.visitMaxs(1, 0);
-      mv.visitEnd();
-    }
-
-    // Write cast method
-    mv = writer.visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC
-        | Opcodes.ACC_SYNTHETIC, SINGLE_JSO_IMPL_CAST_METHOD,
-        "(Ljava/lang/Object;)Ljava/lang/Object;", null, null);
-    if (mv != null) {
-      Label start = new Label();
-      Label end = new Label();
-
-      mv.visitCode();
-      mv.visitLabel(start);
-      // Stack is: empty
-      mv.visitVarInsn(Opcodes.ALOAD, 0);
-      // Stack is: object
-      mv.visitLdcInsn(Type.getType("L" + intfName + ";"));
-      // Stack is: object, interfaceType
-      mv.visitFieldInsn(Opcodes.GETSTATIC, internalName, SINGLE_JSO_IMPL_FIELD,
-          "Ljava/lang/Class;");
-      // Stack is: object, interfaceType, jsoType (may be null)
-
-      mv.visitMethodInsn(Opcodes.INVOKESTATIC, SINGLE_JSO_IMPL_SUPPORT_CLASS,
-          "cast",
-          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;)Ljava/lang/Object;");
-      // Stack is: object (maybe JSO wrapper)
-
-      mv.visitInsn(Opcodes.ARETURN);
-      mv.visitLabel(end);
-      mv.visitMaxs(3, 1);
-      mv.visitLocalVariable("obj", "Ljava/lang/Object;", null, start, end, 0);
-      mv.visitEnd();
-    }
-
-    // Write castToObject method
-    mv = writer.visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC
-        | Opcodes.ACC_SYNTHETIC, SINGLE_JSO_IMPL_CAST_TO_OBJECT_METHOD, "(L"
-        + intfName + ";)Ljava/lang/Object;", null, null);
-    if (mv != null) {
-      Label start = new Label();
-      Label beforeReturn = new Label();
-      Label end = new Label();
-
-      mv.visitCode();
-      mv.visitLabel(start);
-      // Stack is: empty
-      mv.visitVarInsn(Opcodes.ALOAD, 0);
-      // Stack is: object
-      mv.visitTypeInsn(Opcodes.INSTANCEOF, JAVASCRIPTOBJECT_DESC);
-      // Stack is: int
-      mv.visitJumpInsn(Opcodes.IFEQ, beforeReturn);
-      // Stack is: empty
-
-      mv.visitVarInsn(Opcodes.ALOAD, 0);
-      // Stack is: object
-      mv.visitTypeInsn(Opcodes.CHECKCAST, JAVASCRIPTOBJECT_DESC);
-      // Stack is: JSO
-      mv.visitMethodInsn(Opcodes.INVOKESTATIC, JAVASCRIPTOBJECT_DESC,
-          REWRAP_METHOD, "(L" + JAVASCRIPTOBJECT_DESC + ";)L"
-              + JAVASCRIPTOBJECT_DESC + ";");
-      // Stack is: canonical JSO
-      mv.visitVarInsn(Opcodes.ASTORE, 0);
-      // Stack is: empty (local 0 contains canonical object)
-
-      mv.visitLabel(beforeReturn);
-      mv.visitFrame(Opcodes.F_NEW, 1, new Object[] {"java/lang/Object"}, 0,
-          EMPTY_OBJECT_ARRAY);
-      mv.visitVarInsn(Opcodes.ALOAD, 0);
-      mv.visitInsn(Opcodes.ARETURN);
-      mv.visitLabel(end);
-      mv.visitMaxs(1, 1);
-      mv.visitLocalVariable("obj", "Ljava/lang/Object;", null, start, end, 0);
-      mv.visitEnd();
-    }
-
-    // Write instanceOf method
-    mv = writer.visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC
-        | Opcodes.ACC_SYNTHETIC, SINGLE_JSO_IMPL_INSTANCEOF_METHOD,
-        "(Ljava/lang/Object;)Z", null, null);
-    if (mv != null) {
-      Label start = new Label();
-      Label end = new Label();
-
-      mv.visitCode();
-      mv.visitLabel(start);
-      // Stack is: empty
-      mv.visitVarInsn(Opcodes.ALOAD, 0);
-      // Stack is: object
-      mv.visitLdcInsn(Type.getType("L" + intfName + ";"));
-      // Stack is: object, interfaceType
-      mv.visitFieldInsn(Opcodes.GETSTATIC, internalName, SINGLE_JSO_IMPL_FIELD,
-          "Ljava/lang/Class;");
-      // Stack is: object, interfaceType, jsoType (may be null)
-
-      mv.visitMethodInsn(Opcodes.INVOKESTATIC, SINGLE_JSO_IMPL_SUPPORT_CLASS,
-          "instanceOf",
-          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;)Z");
-      // Stack is: boolean
-
-      mv.visitInsn(Opcodes.IRETURN);
-      mv.visitLabel(end);
-      mv.visitMaxs(3, 1);
-      mv.visitLocalVariable("obj", "Ljava/lang/Object;", null, start, end, 0);
-      mv.visitEnd();
-    }
-
-    writer.visitEnd();
-    return writer.toByteArray();
-  }
-
-  private static String toInternalName(String jsoSubtype) {
-    return jsoSubtype.replace('.', '/');
-  }
-
-  private String className;
-  private boolean hasClinit;
-  private String[] interfaces;
-  private final RewriterOracle oracle;
-
-  public WriteSingleJsoSupportCode(ClassVisitor cv, RewriterOracle oracle) {
-    super(cv);
-    this.oracle = oracle;
-  }
-
-  @Override
-  public void visit(int version, int access, String name, String signature,
-      String superName, String[] interfaces) {
-    className = name;
-    if (oracle.isJsoOrSubtype(name)) {
-      this.interfaces = oracle.getAllSuperInterfaces(interfaces);
-    } else {
-      this.interfaces = null;
-    }
-    super.visit(version, access, name, signature, superName, interfaces);
-  }
-
-  /**
-   * Write the interface assignment code and possibly introduce a static
-   * initializer.
-   */
-  @Override
-  public void visitEnd() {
-    if (interfaces != null) {
-      writeSingleJsoImplAssignments();
-
-      if (!hasClinit) {
-        MethodVisitor mv = super.visitMethod(Opcodes.ACC_STATIC, "<clinit>",
-            "()V", null, null);
-        if (mv != null) {
-          mv.visitCode();
-          mv.visitFrame(Opcodes.F_NEW, 0, EMPTY_OBJECT_ARRAY, 0,
-              EMPTY_OBJECT_ARRAY);
-          mv.visitMethodInsn(Opcodes.INVOKESTATIC, className,
-              SINGLE_JSO_IMPL_ASSIGNMENT_METHOD, "()V");
-          mv.visitInsn(Opcodes.RETURN);
-          mv.visitEnd();
-        }
-      }
-    }
-
-    super.visitEnd();
-  }
-
-  /**
-   * Possibly update the existing static initializer to call the assignment
-   * code.
-   */
-  @Override
-  public MethodVisitor visitMethod(int access, String name, String desc,
-      String signature, String[] exceptions) {
-    if (interfaces != null && "<clinit>".equals(name)) {
-      // Disable code in visitEnd()
-      hasClinit = true;
-      MethodVisitor mv = super.visitMethod(access, name, desc, signature,
-          exceptions);
-
-      if (mv == null) {
-        return null;
-      }
-
-      return new MethodAdapter(mv) {
-        /**
-         * Write the call to the assignment method as the first code in the
-         * static initializer.
-         */
-        @Override
-        public void visitCode() {
-          super.visitCode();
-          mv.visitMethodInsn(Opcodes.INVOKESTATIC, className,
-              SINGLE_JSO_IMPL_ASSIGNMENT_METHOD, "()V");
-        }
-      };
-    }
-    return super.visitMethod(access, name, desc, signature, exceptions);
-  }
-
-  /**
-   * Generate code to register the JSO subtype as the implementation type for
-   * its interfaces. For every interface (and super-interface) implemented by
-   * the JSO type, we'll register the JSO type in the interfaces' adjunct types.
-   * <p>
-   * For tag interfaces:
-   * 
-   * <pre>
-   * if (IFoo$singleJsoImpl.singleJsoImpl$ == null) {
-   *   IFoo$singleJsoImpl.singleJsoImpl$ = JsoFoo.class;
-   * }
-   * </pre>
-   * </p>
-   * <p>
-   * For non-trivial interfaces, we check to see if any existing type is a
-   * supertype of this JSO:
-   * 
-   * <pre>
-   * if (IFoo$singleJsoImpl.singleJsoImpl$ == null) {
-   *   IFoo$singleJsoImpl.singleJsoImpl$ = JsoFoo.class;
-   * } else if (!IFoo$singleJsoImpl.singleJsoImpl$.isAssignableFrom(JsoFoo.class) {
-   *   throw new RuntimeException();
-   * }
-   * </pre>
-   * </p>
-   */
-  private void writeSingleJsoImplAssignments() {
-    MethodVisitor mv = super.visitMethod(Opcodes.ACC_PRIVATE
-        | Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC,
-        SINGLE_JSO_IMPL_ASSIGNMENT_METHOD, "()V", null, null);
-    if (mv == null) {
-      return;
-    }
-
-    int stack = 0;
-    mv.visitCode();
-    mv.visitFrame(Opcodes.F_NEW, 0, EMPTY_OBJECT_ARRAY, 0, EMPTY_OBJECT_ARRAY);
-    for (String intf : interfaces) {
-
-      mv.visitFieldInsn(Opcodes.GETSTATIC, intf
-          + SINGLE_JSO_IMPL_ADJUNCT_SUFFIX, SINGLE_JSO_IMPL_FIELD,
-          "Ljava/lang/Class;");
-      // Stack is: classLit (may be null)
-
-      if (oracle.isTagInterface(intf)) {
-        Label noActionNeeded = new Label();
-        /*
-         * Multiple JSO types may implement tag interfaces, so we'll ignore any
-         * existing type.
-         */
-        mv.visitJumpInsn(Opcodes.IFNONNULL, noActionNeeded);
-        mv.visitLdcInsn(Type.getObjectType(className));
-        mv.visitFieldInsn(Opcodes.PUTSTATIC, intf
-            + SINGLE_JSO_IMPL_ADJUNCT_SUFFIX, SINGLE_JSO_IMPL_FIELD,
-            "Ljava/lang/Class;");
-        mv.visitLabel(noActionNeeded);
-        mv.visitFrame(Opcodes.F_NEW, 0, EMPTY_OBJECT_ARRAY, 0,
-            EMPTY_OBJECT_ARRAY);
-        stack = Math.max(stack, 1);
-      } else {
-        /*
-         * Otherwise, throw an exception if the existing JSO implementation is
-         * not a supertype of the current class.
-         */
-        Label noPreviousClass = new Label();
-        Label noActionNeeded = new Label();
-        mv.visitJumpInsn(Opcodes.IFNULL, noPreviousClass);
-        // Stack is: empty
-
-        // Ensure the existing type is one of my supertypes
-        mv.visitFieldInsn(Opcodes.GETSTATIC, intf
-            + SINGLE_JSO_IMPL_ADJUNCT_SUFFIX, SINGLE_JSO_IMPL_FIELD,
-            "Ljava/lang/Class;");
-        mv.visitLdcInsn(Type.getObjectType(className));
-        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Class",
-            "isAssignableFrom", "(Ljava/lang/Class;)Z");
-        mv.visitJumpInsn(Opcodes.IFNE, noActionNeeded);
-        // Stack is: empty
-
-        mv.visitTypeInsn(Opcodes.NEW, "java/lang/RuntimeException");
-        // Stack is: uninitialized
-        mv.visitInsn(Opcodes.DUP);
-        // Stack is: uninitialized, uninitialized
-        mv.visitLdcInsn("Multiple JavaScriptObject subclasses implement an "
-            + "interface declared on this type");
-        // Stack is: uninitialized, uninitialized, string
-        mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/RuntimeException",
-            "<init>", "(Ljava/lang/String;)V");
-        // Stack is: RuntimeException
-        mv.visitInsn(Opcodes.ATHROW);
-
-        mv.visitLabel(noPreviousClass);
-        // Stack is: empty
-        mv.visitFrame(Opcodes.F_NEW, 0, EMPTY_OBJECT_ARRAY, 0,
-            EMPTY_OBJECT_ARRAY);
-        mv.visitLdcInsn(Type.getObjectType(className));
-        // Stack is: class literal
-        mv.visitFieldInsn(Opcodes.PUTSTATIC, intf
-            + SINGLE_JSO_IMPL_ADJUNCT_SUFFIX, SINGLE_JSO_IMPL_FIELD,
-            "Ljava/lang/Class;");
-        // Stack is: empty
-
-        mv.visitLabel(noActionNeeded);
-        // Stack is: empty
-        mv.visitFrame(Opcodes.F_NEW, 0, EMPTY_OBJECT_ARRAY, 0,
-            EMPTY_OBJECT_ARRAY);
-
-        stack = Math.max(stack, 3);
-      }
-    }
-    mv.visitInsn(Opcodes.RETURN);
-    mv.visitMaxs(stack, 0);
-    mv.visitEnd();
-  }
-}
diff --git a/dev/core/src/com/google/gwt/dev/util/Name.java b/dev/core/src/com/google/gwt/dev/util/Name.java
index 647a9f9..279a744 100644
--- a/dev/core/src/com/google/gwt/dev/util/Name.java
+++ b/dev/core/src/com/google/gwt/dev/util/Name.java
@@ -165,12 +165,7 @@
         assert isInternalName(internalName);
         return internalName.replace('/', '.');
       }
-
-      public static String toIdentifier(String internalName) {
-        assert isInternalName(internalName);
-        return internalName.replace("_", "_1").replace('/', '_');
-      }
-
+      
       public static String toSourceName(String internalName) {
         assert isInternalName(internalName);
         // don't change a trailing $ or slash to a .
diff --git a/user/src/com/google/gwt/i18n/client/CurrencyData.java b/user/src/com/google/gwt/i18n/client/CurrencyData.java
index 2b11211..e4eda01 100644
--- a/user/src/com/google/gwt/i18n/client/CurrencyData.java
+++ b/user/src/com/google/gwt/i18n/client/CurrencyData.java
@@ -15,13 +15,9 @@
  */
 package com.google.gwt.i18n.client;
 
-import com.google.gwt.core.client.SingleJsoImpl;
-import com.google.gwt.i18n.client.impl.CurrencyDataImpl;
-
 /**
  * Information about a currency.
  */
-@SingleJsoImpl(CurrencyDataImpl.class)
 public interface CurrencyData {
 
   /**
diff --git a/user/test/com/google/gwt/dev/jjs/test/JsoTest.java b/user/test/com/google/gwt/dev/jjs/test/JsoTest.java
index 93f9e3f..5bf60b0 100644
--- a/user/test/com/google/gwt/dev/jjs/test/JsoTest.java
+++ b/user/test/com/google/gwt/dev/jjs/test/JsoTest.java
@@ -15,13 +15,9 @@
  */
 package com.google.gwt.dev.jjs.test;
 
-import com.google.gwt.core.client.GWT;
 import com.google.gwt.core.client.JavaScriptObject;
 import com.google.gwt.junit.client.GWTTestCase;
 
-import java.util.Arrays;
-import java.util.List;
-
 /**
  * Tests {@link JavaScriptObject} and subclasses.
  */
@@ -56,7 +52,7 @@
     public static native String staticNative() /*-{
       return "nativeFoo";
     }-*/;
-
+    
     /**
      * Ensure that a supertype can refer to members of a subtype.
      */
@@ -84,7 +80,7 @@
     static String staticValueSub() {
       return "FooSub";
     }
-
+    
     protected FooSub() {
     }
 
@@ -211,20 +207,6 @@
     }
   }
 
-  public static void assertSame(Object o1, Object o2) {
-    assertTrue("Failed double-equals", o1 == o2);
-    assertTrue("Failed o1.equals(o2)", o1.equals(o2));
-    assertTrue("Failed o2.equals(o1)", o2.equals(o1));
-    assertEquals("Hashcode mismatch", o1.hashCode(), o2.hashCode());
-  }
-
-  public static void assertSame(JavaScriptObject o1, JavaScriptObject o2) {
-    assertTrue("Failed double-equals", o1 == o2);
-    assertTrue("Failed o1.equals(o2)", o1.equals(o2));
-    assertTrue("Failed o2.equals(o1)", o2.equals(o1));
-    assertEquals("Hashcode mismatch", o1.hashCode(), o2.hashCode());
-  }
-
   private static native Bar makeBar() /*-{
     return {
       toString:function() {
@@ -275,8 +257,6 @@
     return jso;
   }-*/;
 
-  private Object aField;
-
   @Override
   public String getModuleName() {
     return "com.google.gwt.dev.jjs.CompilerSuite";
@@ -289,54 +269,6 @@
     assertFalse(array[2] instanceof JavaScriptObject);
   }
 
-  public void testArrayJreInteractions() {
-    JavaScriptObject obj = makeJSO();
-    Foo[] foo = new Foo[1];
-    foo[0] = (Foo) obj;
-
-    Bar[] bar = new Bar[1];
-    bar[0] = (Bar) obj;
-
-    assertSame(foo[0], bar[0]);
-
-    List<Foo> fooList = Arrays.asList(foo);
-    List<Bar> barList = Arrays.asList(bar);
-    assertSame(fooList.get(0), barList.get(0));
-    assertSame(fooList.iterator().next(), barList.iterator().next());
-
-    @SuppressWarnings("unchecked")
-    List fooRawList = Arrays.asList(foo);
-    @SuppressWarnings("unchecked")
-    List barRawList = Arrays.asList(bar);
-    assertSame(fooRawList.get(0), barRawList.get(0));
-    assertSame(fooRawList.iterator().next(), barRawList.iterator().next());
-
-    // Test Collection.toArray(), since the behavior depends on the array type
-    Object[] objectArray = fooList.toArray(new Object[0]);
-    assertSame(foo[0], objectArray[0]);
-    JavaScriptObject[] jsArray = fooList.toArray(new JavaScriptObject[0]);
-    assertSame(foo[0], jsArray[0]);
-    Foo[] fooArray = fooList.toArray(new Foo[0]);
-    assertSame(foo[0], fooArray[0]);
-    acceptsFooArray(fooArray, 1);
-
-    // Test System.arrayCopy to make sure cross-JSO cast appears to work
-    Bar[] bar2 = new Bar[1];
-    System.arraycopy(foo, 0, bar2, 0, 1);
-    assertSame(foo[0], bar[0]);
-
-    try {
-      String[] bad = new String[1];
-      System.arraycopy(foo, 0, bad, 0, 1);
-      fail("Should have thrown ArrayStoreException");
-    } catch (ArrayStoreException expected) {
-    }
-  }
-
-  private void acceptsFooArray(Foo[] array, int length) {
-    assertEquals(length, array.length);
-  }
-
   public void testArrayStore() {
     JavaScriptObject[] jsoArray = new JavaScriptObject[1];
     jsoArray[0] = makeJSO();
@@ -442,16 +374,6 @@
       fail("Expected ClassCastException");
     } catch (ClassCastException expected) {
     }
-
-    bar = null;
-    assertFalse(bar instanceof JavaScriptObject);
-    try {
-      assertNull(bar);
-      foo = bar.cast();
-      assertNull(foo);
-    } catch (RuntimeException e) {
-      fail("Should not have thrown exception " + e.getMessage());
-    }
   }
 
   @SuppressWarnings("cast")
@@ -508,10 +430,7 @@
     assertEquals(JavaScriptObject.class, Bar.class);
     assertEquals(Foo.class, Bar.class);
 
-    if (!GWT.isScript()) {
-      assertEquals("com.google.gwt.core.client.JavaScriptObject",
-          JavaScriptObject.class.getName());
-    } else if (!JavaScriptObject.class.getName().startsWith("Class$")) {
+    if (!JavaScriptObject.class.getName().startsWith("Class$")) {
       // Class metadata could be disabled
       assertEquals("com.google.gwt.core.client.JavaScriptObject$",
           JavaScriptObject.class.getName());
@@ -535,10 +454,7 @@
     assertEquals(JavaScriptObject[][].class, Bar[][].class);
     assertEquals(Foo[][].class, Bar[][].class);
 
-    if (!GWT.isScript()) {
-      assertEquals("[[Lcom.google.gwt.core.client.JavaScriptObject;",
-          JavaScriptObject[][].class.getName());
-    } else if (!JavaScriptObject.class.getName().startsWith("Class$")) {
+    if (!JavaScriptObject.class.getName().startsWith("Class$")) {
       // Class metadata could be disabled
       assertEquals("[[Lcom.google.gwt.core.client.JavaScriptObject$;",
           JavaScriptObject[][].class.getName());
@@ -550,19 +466,11 @@
     assertEquals(jso, jso);
 
     JavaScriptObject jso2 = makeJSO();
-    assertNotSame(jso, jso2);
     assertFalse(jso.equals(jso2));
     assertFalse(jso2.equals(jso));
 
     jso2 = returnMe(jso);
-    assertSame(jso, jso2);
-
-    Object aLocal = (Bar) jso;
-    Object aLocal2 = (Foo) jso2;
-    aField = aLocal;
-    assertSame(aLocal, aField);
-    assertSame(jso, aField);
-    assertSame(aLocal, aLocal2);
+    assertEquals(jso, jso2);
   }
 
   public void testGenericsJsos() {
@@ -654,30 +562,6 @@
 
     jso2 = returnMe(jso);
     assertSame(jso, jso2);
-
-    {
-      JavaScriptObject[] arr = new JavaScriptObject[] {jso, jso2};
-      assertSame(arr[0], arr[1]);
-    }
-
-    {
-      JavaScriptObject[] arr = new JavaScriptObject[2];
-      arr[0] = jso;
-      arr[1] = jso2;
-      assertSame(arr[0], arr[1]);
-    }
-
-    {
-      JavaScriptObject[] arr = new JavaScriptObject[] {(Foo) jso, (Bar) jso2};
-      assertSame(arr[0], arr[1]);
-    }
-
-    {
-      JavaScriptObject[] arr = new JavaScriptObject[2];
-      arr[0] = (Foo) jso;
-      arr[1] = (Bar) jso2;
-      assertSame(arr[0], arr[1]);
-    }
   }
 
   public void testInheritance() {
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 b7d4927..ae07751 100644
--- a/user/test/com/google/gwt/dev/jjs/test/SingleJsoImplTest.java
+++ b/user/test/com/google/gwt/dev/jjs/test/SingleJsoImplTest.java
@@ -16,20 +16,12 @@
 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.dev.jjs.test.jsointfs.JsoInterfaceWithUnreferencedImpl;
 import com.google.gwt.junit.client.GWTTestCase;
 import com.google.gwt.user.client.rpc.AsyncCallback;
 
 import java.io.IOException;
-import java.util.AbstractMap;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.IdentityHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
 
 /**
  * Ensures that JavaScriptObjects may implement interfaces with methods.
@@ -75,12 +67,10 @@
     String call(int a, int b);
   }
 
-  @SingleJsoImpl(JsoCreatedWithCast.class)
   interface CreatedWithCast {
     String foo();
   }
 
-  @SingleJsoImpl(JsoCreatedWithCastToTag.class)
   interface CreatedWithCastToTag {
   }
 
@@ -519,19 +509,12 @@
     return {};
   }-*/;
 
-  private Object asObject;
-
-  private Simple asSimple;
-
-  private Adder asAdder;
-
   @Override
   public String getModuleName() {
     return "com.google.gwt.dev.jjs.CompilerSuite";
   }
 
   public void testCallsToInnerTypes() {
-    JsoHasInnerJsoType o = JavaScriptObject.createObject().cast();
     CallsMethodInInnerType a = (CallsMethodInInnerType) JavaScriptObject.createObject();
     InnerType i = (InnerType) JavaScriptObject.createObject();
     assertEquals(5, a.call(i, 5).get());
@@ -539,7 +522,6 @@
   }
 
   public void testCallsWithArrays() {
-    JsoUsesArrays toss = JavaScriptObject.createObject().cast();
     UsesArrays a = JavaScriptObject.createObject().<JsoUsesArrays> cast();
     a.acceptIntArray(a.returnIntArray());
     a.acceptInt3Array(a.returnInt3Array());
@@ -557,12 +539,6 @@
     a.acceptObject3Array(a.returnString3Array());
   }
 
-  public void testClassLiterals() {
-    JavaScriptObject jso = makeSimple();
-    assertEquals(JavaScriptObject.class, jso.getClass());
-    assertEquals(JavaScriptObject.class, asSimple((JsoSimple) jso).getClass());
-  }
-
   /**
    * Ensure that SingleJSO types that are referred to only via a cast to the
    * interface type are retained. If the JsoCreatedWithCast type isn't rescued
@@ -570,7 +546,6 @@
    * compiler would assume there are types that implement the interface.
    */
   public void testCreatedWithCast() {
-    // This can't work in hosted mode, we need something to load the JSO
     try {
       Object a = (CreatedWithCast) JavaScriptObject.createObject();
     } catch (ClassCastException e) {
@@ -582,10 +557,6 @@
       fail("b");
     }
   }
-  
-  public void testDispatchWithWidenedArrays() {
-    acceptsSimpleArrayAndInvokes(makeSimple(), makeSimple(), makeSimple());
-  }
 
   public void testDualCase() {
     // Direct dispatch
@@ -633,7 +604,7 @@
       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);
+      assertTrue(a instanceof Tag);
       try {
         ((JavaAdder) a).add(1, 1);
         fail("Should have thrown CCE");
@@ -665,269 +636,6 @@
     }, "Hello");
   }
 
-  /**
-   * Test identity with arrays.
-   */
-  public void testIdentityArrays() {
-    // XXX Really need @SingleJsoImpl annotation here
-    makeDivider(1);
-    makeAdder(1);
-    JavaScriptObject obj = makeSimple();
-    Adder[] adders = {(Adder) obj};
-    Divider[] dividers = {(Divider) obj};
-    assertSame(adders[0], dividers[0]);
-
-    Object[] arr = {adders[0], dividers[0]};
-    assertSame(arr[0], arr[1]);
-
-    List<Adder> adderList = Arrays.asList(adders);
-    List<Divider> dividerList = Arrays.asList(dividers);
-    assertSame(adderList.get(0), dividerList.get(0));
-
-    @SuppressWarnings("unchecked")
-    List adderListRaw = Arrays.asList(adders);
-    @SuppressWarnings("unchecked")
-    List dividerListRaw = Arrays.asList(dividers);
-    assertSame(adderListRaw.get(0), dividerListRaw.get(0));
-  }
-
-  /**
-   * Pass some SingleJsoImpl types into JRE collections.
-   */
-  public void testIdentityJRE() {
-    IdentityHashMap<JavaScriptObject, Boolean> jMap = new IdentityHashMap<JavaScriptObject, Boolean>();
-    IdentityHashMap<Simple, Boolean> sMap = new IdentityHashMap<Simple, Boolean>();
-
-    JsoSimple jso = makeSimple();
-    jMap.put(jso, true);
-    sMap.put(jso, true);
-
-    assertTrue(jMap.get(jso));
-    assertTrue(sMap.get(jso));
-
-    assertTrue(jMap.get(asSimple(jso)));
-    assertTrue(sMap.get(asSimple(jso)));
-    assertTrue(jMap.get(asSimple));
-    assertTrue(sMap.get(asSimple));
-
-    assertTrue(jMap.get(asObject(jso)));
-    assertTrue(sMap.get(asObject(jso)));
-    assertTrue(jMap.get(asObject));
-    assertTrue(sMap.get(asObject));
-
-    assertNull(jMap.get(JavaScriptObject.createObject()));
-    assertNull(sMap.get(JavaScriptObject.createObject()));
-  }
-
-  /**
-   * Pass some SingleJsoImpl types into an IdentityHashMap.
-   */
-  public void testIdentityJRE2() {
-    final JavaScriptObject jso = JavaScriptObject.createObject();
-    IdentityHashMap<Object, Boolean> map = new IdentityHashMap<Object, Boolean>();
-
-    // First with a proper JSO subtype
-    map.putAll(new AbstractMap<JsoAdder, Boolean>() {
-      @Override
-      public Set<Entry<JsoAdder, Boolean>> entrySet() {
-        Entry<JsoAdder, Boolean> entry = new Entry<JsoAdder, Boolean>() {
-          public JsoAdder getKey() {
-            return (JsoAdder) jso;
-          }
-
-          public Boolean getValue() {
-            return true;
-          }
-
-          public Boolean setValue(Boolean value) {
-            throw new RuntimeException("unimplemented");
-          }
-        };
-        return Collections.singleton(entry);
-      }
-    });
-
-    assertEquals(1, map.size());
-    assertNotNull(map.get(jso));
-    assertTrue(map.get(jso));
-    assertTrue(map.get((Adder) jso));
-    assertTrue(map.get(asAdder((Adder) jso)));
-    assertTrue(map.get((JsoAdder) jso));
-    assertTrue(map.get((JsoSimple) jso));
-
-    // Now with an interface type
-    map.putAll(new AbstractMap<Adder, Boolean>() {
-      @Override
-      public Set<Entry<Adder, Boolean>> entrySet() {
-        Entry<Adder, Boolean> entry = new Entry<Adder, Boolean>() {
-          public Adder getKey() {
-            return (Adder) jso;
-          }
-
-          public Boolean getValue() {
-            return false;
-          }
-
-          public Boolean setValue(Boolean value) {
-            throw new RuntimeException("unimplemented");
-          }
-        };
-        return Collections.singleton(entry);
-      }
-    });
-
-    assertEquals(1, map.size());
-    assertNotNull(map.get(jso));
-    assertFalse(map.get(jso));
-
-    assertNull(map.get(JavaScriptObject.createObject()));
-
-    map.put(JavaScriptObject.createObject(), true);
-    assertFalse(map.remove(jso));
-    assertEquals(1, map.size());
-    assertTrue(map.values().iterator().next());
-  }
-
-  public void testIdentityMethodsAndFields() {
-    JsoSimple jso = makeSimple();
-    JsoSimple jso2 = makeSimple();
-    assertNotSame(jso, jso2);
-    assertTrue(jso != jso2);
-    assertFalse(jso == jso2);
-
-    Object o = asObject(jso);
-    Object o2 = asObject(jso2);
-    assertNotSame(o, o2);
-    assertTrue(o != o2);
-    assertFalse(o == o2);
-
-    assertSame(jso, o);
-    assertTrue(jso == o);
-    assertFalse(jso != o);
-
-    assertSame(jso2, o2);
-    assertTrue(jso2 == o2);
-    assertFalse(jso2 != o2);
-
-    assertSame(asObject, jso2);
-    assertTrue(asObject == jso2);
-    assertFalse(asObject != jso2);
-
-    assertSame(asObject, o2);
-    assertTrue(asObject == o2);
-    assertFalse(asObject != o2);
-
-    Simple s = asSimple(jso);
-    Simple s2 = asSimple(jso2);
-    assertNotSame(s, s2);
-    assertTrue(s != s2);
-    assertFalse(s == s2);
-
-    assertSame(jso, s);
-    assertTrue(jso == s);
-    assertFalse(jso != s);
-
-    assertSame(s, o);
-    assertTrue(s == o);
-    assertFalse(s != o);
-
-    assertSame(jso2, s2);
-    assertTrue(jso2 == s2);
-    assertFalse(jso2 != s2);
-
-    assertSame(o2, s2);
-    assertTrue(o2 == s2);
-    assertFalse(o2 != s2);
-
-    assertSame(asSimple, jso2);
-    assertTrue(asSimple == jso2);
-    assertFalse(asSimple != jso2);
-
-    assertSame(asSimple, o2);
-    assertTrue(asSimple == o2);
-    assertFalse(asSimple != o2);
-
-    assertSame(asSimple, s2);
-    assertTrue(asSimple == s2);
-    assertFalse(asSimple != s2);
-
-    JsoRandom r = (JsoRandom) o2;
-    assertSame(r, jso2);
-    // Can't legally compare r == jso2
-
-    assertSame(r, o2);
-    assertTrue(r == o2);
-    assertFalse(r != o2);
-
-    assertSame(r, asObject);
-    assertTrue(r == asObject);
-    assertFalse(r != asObject);
-
-    assertSame(r, s2);
-    // Can't legally compare r == s2
-
-    assertSame(r, asSimple);
-    // Can't legally compare r == asSimple
-  }
-
-  public void testIdentityWithDualTypes() {
-    JsoAdder jso = makeAdder(0);
-    JavaAdder java = new JavaAdder();
-
-    assertNotSame(jso, java);
-
-    IdentityHashMap<Adder, Integer> map = new IdentityHashMap<Adder, Integer>();
-    map.put(jso, 0);
-    map.put(java, 1);
-    assertEquals(2, map.size());
-
-    assertEquals(new Integer(0), map.get(jso));
-    assertEquals(new Integer(1), map.get(java));
-
-    // Use unambiguous dispatch
-    assertEquals(new Integer(0), map.get(asAdder(jso)));
-    assertEquals(new Integer(0), map.get(asAdder));
-    assertEquals(new Integer(1), map.get(asAdder(java)));
-    assertEquals(new Integer(1), map.get(asAdder));
-
-    // Use ambiguous dispatch
-    assertEquals(new Integer(0), map.get(asAdder((Adder) jso)));
-    assertEquals(new Integer(0), map.get(asAdder));
-    assertEquals(new Integer(1), map.get(asAdder((Adder) java)));
-    assertEquals(new Integer(1), map.get(asAdder));
-
-    // Test with plain Object
-    assertEquals(new Integer(0), map.get(asObject(jso)));
-    assertEquals(new Integer(0), map.get(asObject));
-    assertEquals(new Integer(1), map.get(asObject(java)));
-    assertEquals(new Integer(1), map.get(asObject));
-
-    // Behavior of keys
-    for (Map.Entry<Adder, Integer> entry : map.entrySet()) {
-      if (entry.getKey() == jso) {
-        assertEquals(new Integer(0), entry.getValue());
-      }
-      if (entry.getKey() == java) {
-        assertEquals(new Integer(1), entry.getValue());
-      }
-    }
-  }
-
-  public void testIdentityWithNativeMethods() {
-    JsoSimple s = makeSimple();
-    Simple s2 = asSimpleNative(s);
-    assertSame(s, s2);
-    assertSame(s, asSimple);
-    assertSame(s2, asSimple);
-  }
-
-  /**
-   * Called from asSimpleNative to continue the above test.
-   */
-  public void testIdentityWithNativeMethods(JsoSimple o) {
-    assertSame(o, asSimple);
-  }
-
   @SuppressWarnings("cast")
   public void testSimpleCase() {
     {
@@ -1003,7 +711,6 @@
   }
 
   public void testStaticCallsToSubclasses() {
-    JsoCallsStaticMethodInSubclass a = JavaScriptObject.createObject().cast();
     Object o = "String";
     assertEquals(String.class, o.getClass());
     o = JavaScriptObject.createObject();
@@ -1084,66 +791,9 @@
     }
   }
 
-  /**
-   * This test relies on the SingleJsoimpl annotation in dev mode.
-   */
   public void testUnreferencedType() {
     JsoInterfaceWithUnreferencedImpl o = (JsoInterfaceWithUnreferencedImpl) JavaScriptObject.createObject();
     assertNotNull(o);
     assertTrue(o.isOk());
   }
-
-  protected Adder asAdder(Adder o) {
-    asAdder = o;
-    return o;
-  }
-
-  /*
-   * These asXYZ methods are to test cast-via-return and cast-via-assignment
-   * rewriting.
-   */
-
-  protected Adder asAdder(JavaAdder o) {
-    asAdder = o;
-    return o;
-  }
-
-  protected Adder asAdder(JsoAdder o) {
-    asAdder = o;
-    return o;
-  }
-
-  protected Object asObject(JavaAdder jso) {
-    asObject = jso;
-    return jso;
-  }
-
-  protected Object asObject(JsoAdder jso) {
-    asObject = jso;
-    return jso;
-  }
-
-  protected Object asObject(JsoSimple jso) {
-    asObject = jso;
-    return jso;
-  }
-
-  protected Simple asSimple(JsoSimple jso) {
-    asSimple = jso;
-    return jso;
-  }
-
-  private void acceptsSimpleArrayAndInvokes(Simple... simples) {
-    assertEquals(3, simples.length);
-    for (Simple s : simples) {
-      assertEquals("a", s.a());
-    }
-  }
-
-  private native Simple asSimpleNative(Object o) /*-{
-    // Make sure glue code does the interface cast
-    this.@com.google.gwt.dev.jjs.test.SingleJsoImplTest::asSimple = o;
-    this.@com.google.gwt.dev.jjs.test.SingleJsoImplTest::testIdentityWithNativeMethods(Lcom/google/gwt/dev/jjs/test/SingleJsoImplTest$JsoSimple;)(o);
-    return o;
-  }-*/;
 }
diff --git a/user/test/com/google/gwt/dev/jjs/test/jsointfs/JsoInterfaceWithUnreferencedImpl.java b/user/test/com/google/gwt/dev/jjs/test/jsointfs/JsoInterfaceWithUnreferencedImpl.java
index 8a281e0..42a7e26 100644
--- a/user/test/com/google/gwt/dev/jjs/test/jsointfs/JsoInterfaceWithUnreferencedImpl.java
+++ b/user/test/com/google/gwt/dev/jjs/test/jsointfs/JsoInterfaceWithUnreferencedImpl.java
@@ -15,14 +15,10 @@
  */
 package com.google.gwt.dev.jjs.test.jsointfs;
 
-import com.google.gwt.core.client.SingleJsoImpl;
-import com.google.gwt.dev.jjs.test.jsoimpls.UnreferencedImplOfJsoInterface;
-
 /**
  * This class exists for the purpose of testing JSO implementation types that
  * aren't specifically referenced in any Java source.
  */
-@SingleJsoImpl(UnreferencedImplOfJsoInterface.class)
 public interface JsoInterfaceWithUnreferencedImpl {
   boolean isOk();
 }