Revision: 6412
Author: [email protected]
Date: Sun Oct 18 11:48:45 2009
Log: Fix SingleJsoImpl hosted mode crash with contravariant return types in  
virtual
override scenario.

Put simply

class B extends A{}

interface I {
   A returnsA();
}

class Jso extends JavaScriptObject {
   B returnsA();
}

class JsoSub extends Jso implements I {}

crashes in CCL$MyInstanceMethodOracle.findOriginalDeclaringClass().

Web mode already handles this correctly.

Patch by: bobv
Review by: scottb


http://code.google.com/p/google-web-toolkit/source/detail?r=6412

Modified:
  /trunk/dev/core/src/com/google/gwt/dev/shell/CompilingClassLoader.java
   
/trunk/dev/core/src/com/google/gwt/dev/shell/rewrite/HostedModeClassRewriter.java
   
/trunk/dev/core/src/com/google/gwt/dev/shell/rewrite/RewriteSingleJsoImplDispatches.java
  /trunk/dev/core/src/com/google/gwt/dev/shell/rewrite/WriteJsoImpl.java
   
/trunk/user/test/com/google/gwt/dev/jjs/test/singlejso/TypeHierarchyTest.java

=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/shell/CompilingClassLoader.java      
 
Fri Sep 25 11:27:03 2009
+++ /trunk/dev/core/src/com/google/gwt/dev/shell/CompilingClassLoader.java      
 
Sun Oct 18 11:48:45 2009
@@ -35,7 +35,9 @@
  import com.google.gwt.dev.shell.rewrite.HasAnnotation;
  import com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter;
  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.InternalName;
  import com.google.gwt.dev.util.Name.SourceOrBinaryName;
@@ -61,9 +63,9 @@
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
-import java.util.SortedMap;
+import java.util.SortedSet;
  import java.util.Stack;
-import java.util.TreeMap;
+import java.util.TreeSet;

  /**
   * An isolated {...@link ClassLoader} for running all user code. All user  
files are
@@ -360,21 +362,34 @@
      private final Map<String, Set<JClassType>> signatureToDeclaringClasses  
= new HashMap<String, Set<JClassType>>();

      public MyInstanceMethodOracle(Set<JClassType> jsoTypes,
-        JClassType javaLangObject) {
-      // Populate the map.
+        JClassType javaLangObject, SingleJsoImplData jsoData) {
+
+      // Record that the JSO implements its own methods
        for (JClassType type : jsoTypes) {
          for (JMethod method : type.getMethods()) {
            if (!method.isStatic()) {
-            String signature = createSignature(method);
-            Set<JClassType> declaringClasses =  
signatureToDeclaringClasses.get(signature);
-            if (declaringClasses == null) {
-              declaringClasses = new HashSet<JClassType>();
-              signatureToDeclaringClasses.put(signature, declaringClasses);
-            }
-            declaringClasses.add(type);
+            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()) {
@@ -389,6 +404,7 @@
      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());
@@ -412,6 +428,20 @@
        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();
@@ -430,6 +460,177 @@
        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, com.google.gwt.dev.asm.commons.Method>  
mangledNamesToDeclarations = new HashMap<String,  
com.google.gwt.dev.asm.commons.Method>();
+    private final Map<String, com.google.gwt.dev.asm.commons.Method>  
mangledNamesToImplementations = new HashMap<String,  
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);
+
+          JType[] parameterTypes = new  
JType[intfMethod.getParameters().length];
+          for (int i = 0; i < parameterTypes.length; i++) {
+            parameterTypes[i] = intfMethod.getParameters()[i].getType();
+          }
+
+          /*
+           * 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 = implementingType.findMethod(
+              intfMethod.getName(), parameterTypes)) == null) {
+            implementingType = implementingType.getSuperclass();
+          }
+          assert implementingMethod != null && implementingType !=  
null : "Unable to find virtual override for "
+              + intfMethod.toString();
+
+          /*
+           * 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())
+                + " " + intfMethod.getName() + "(";
+            for (JType paramType : parameterTypes) {
+              decl += ",";
+              decl += getBinaryOrPrimitiveName(paramType);
+            }
+            decl += ")";
+
+            com.google.gwt.dev.asm.commons.Method declaration =  
com.google.gwt.dev.asm.commons.Method.getMethod(decl);
+            mangledNamesToDeclarations.put(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());
+            String jsoName = getBinaryOrPrimitiveName(implementingType);
+
+            String decl = returnName + " " + intfMethod.getName() + "$ ("
+                + jsoName;
+            for (JType paramType : parameterTypes) {
+              decl += ",";
+              decl += getBinaryOrPrimitiveName(paramType);
+            }
+            decl += ")";
+
+            com.google.gwt.dev.asm.commons.Method toImplement =  
com.google.gwt.dev.asm.commons.Method.getMethod(decl);
+            mangledNamesToImplementations.put(mangledName, toImplement);
+          }
+        }
+      }
+
+      if (logger.isLoggable(Type.SPAM)) {
+        TreeLogger dumpLogger = logger.branch(Type.SPAM,
+            "SingleJsoImpl method mappings");
+        for (Map.Entry<String, com.google.gwt.dev.asm.commons.Method>  
entry : mangledNamesToImplementations.entrySet()) {
+          dumpLogger.log(Type.SPAM, entry.getKey() + " -> " +  
entry.getValue());
+        }
+      }
+    }
+
+    public com.google.gwt.dev.asm.commons.Method getDeclaration(
+        String mangledName) {
+      return mangledNamesToDeclarations.get(mangledName);
+    }
+
+    public com.google.gwt.dev.asm.commons.Method getImplementation(
+        String mangledName) {
+      return mangledNamesToImplementations.get(mangledName);
+    }
+
+    public SortedSet<String> getMangledNames() {
+      return unmodifiableNames;
+    }
+
+    public Set<String> getSingleJsoIntfTypes() {
+      return unmodifiableIntfNames;
+    }
+  }

    /**
     * The names of the bridge classes.
@@ -643,15 +844,12 @@
          jsoSuperTypes.put(binaryName, types);
        }

-      // computeSingleJsoImplData has two out parameters
-      SortedMap<String, com.google.gwt.dev.asm.commons.Method>  
mangledNamesToImplementations = new TreeMap<String,  
com.google.gwt.dev.asm.commons.Method>();
-      computeSingleJsoImplData(singleJsoImplTypes,
-          mangledNamesToImplementations);
+      SingleJsoImplData singleJsoImplData = new MySingleJsoImplData();

        MyInstanceMethodOracle mapper = new MyInstanceMethodOracle(jsoTypes,
-          typeOracle.getJavaLangObject());
+          typeOracle.getJavaLangObject(), singleJsoImplData);
        classRewriter = new HostedModeClassRewriter(jsoTypeNames,  
jsoSuperTypes,
-          mangledNamesToImplementations, singleJsoImplTypes, mapper);
+          singleJsoImplData, mapper);
      } else {
        // If we couldn't find the JSO class, we don't need to do any  
rewrites.
        classRewriter = null;
@@ -837,125 +1035,6 @@
      }
      return lookupClassName;
    }
-
-  /**
-   * 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 void computeSingleJsoImplData(
-      Set<String> singleJsoImplTypes,
-      SortedMap<String, com.google.gwt.dev.asm.commons.Method>  
mangledNamesToImplementations) {
-
-    // Loop over all types declared with the SingleJsoImpl annotation
-    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 m : type.getOverridableMethods()) {
-        assert m.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(m.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('.', '_') + "_"
-            + m.getName();
-
-        JType[] parameterTypes = new JType[m.getParameters().length];
-        for (int i = 0; i < parameterTypes.length; i++) {
-          parameterTypes[i] = m.getParameters()[i].getType();
-        }
-
-        /*
-         * Handle virtual overrides by finding the method that we would  
normally
-         * invoke and using its declaring class as the dispatch target.
-         */
-        while (implementingType.findMethod(m.getName(), parameterTypes) ==  
null) {
-          implementingType = implementingType.getSuperclass();
-        }
-        assert implementingType != null : "Unable to find virtual override  
for "
-            + m.toString();
-
-        /*
-         * Cook up the a pseudo-method declaration for the concrete type.  
This
-         * should look something like
-         *
-         * ReturnType method$ (JsoType, ParamType, ParamType)
-         *
-         * This must be kept in sync with the WriteJsoImpl class.
-         */
-        String decl = getBinaryOrPrimitiveName(m.getReturnType()) + " "
-            + m.getName() + "$ (" +  
getBinaryOrPrimitiveName(implementingType);
-        for (JType paramType : parameterTypes) {
-          decl += ",";
-          decl += getBinaryOrPrimitiveName(paramType);
-        }
-        decl += ")";
-
-        com.google.gwt.dev.asm.commons.Method toImplement =  
com.google.gwt.dev.asm.commons.Method.getMethod(decl);
-
-        mangledNamesToImplementations.put(mangledName, toImplement);
-      }
-    }
-
-    if (logger.isLoggable(Type.SPAM)) {
-      TreeLogger dumpLogger = logger.branch(Type.SPAM,
-          "SingleJsoImpl method mappings");
-      for (Map.Entry<String, com.google.gwt.dev.asm.commons.Method>  
entry : mangledNamesToImplementations.entrySet()) {
-        dumpLogger.log(Type.SPAM, entry.getKey() + " -> " +  
entry.getValue());
-      }
-    }
-  }

    private byte[] findClassBytes(String className) {
      if (JavaScriptHost.class.getName().equals(className)) {
=======================================
---  
/trunk/dev/core/src/com/google/gwt/dev/shell/rewrite/HostedModeClassRewriter.java
        
Tue May 26 11:56:11 2009
+++  
/trunk/dev/core/src/com/google/gwt/dev/shell/rewrite/HostedModeClassRewriter.java
        
Sun Oct 18 11:48:45 2009
@@ -30,7 +30,7 @@
  import java.util.ListIterator;
  import java.util.Map;
  import java.util.Set;
-import java.util.SortedMap;
+import java.util.SortedSet;

  /**
   * This class performs any and all byte code rewriting needed to make  
hosted
@@ -85,6 +85,33 @@
       */
      String findOriginalDeclaringClass(String declaredClass, String  
signature);
    }
+
+  /**
+   * Contains data about how SingleJsoImpl methods are to be dispatched.
+   */
+  public interface SingleJsoImplData {
+    /**
+     * Returns a Method corresponding to the declaration of the abstract  
method
+     * in an interface type.
+     */
+    Method getDeclaration(String mangledName);
+
+    /**
+     * Return a Method corresponding to the concrete implementation of the
+     * method in a JSO type.
+     */
+    Method getImplementation(String mangledName);
+
+    /**
+     * Returns all of the mangled method names for SingleJsoImpl methods.
+     */
+    SortedSet<String> getMangledNames();
+
+    /**
+     * Returns the internal names of all interface types implemented by  
JSOs.
+     */
+    Set<String> getSingleJsoIntfTypes();
+  }

    static final String JAVASCRIPTOBJECT_DESC =  
JsValueGlue.JSO_CLASS.replace(
        '.', '/');
@@ -114,6 +141,8 @@
     */
    private final Set<String> jsoIntfDescs;

+  private final SingleJsoImplData jsoData;
+
    /**
     * Records the superclass of every JSO for generating empty JSO  
interfaces.
     */
@@ -124,10 +153,6 @@
     */
    private InstanceMethodOracle mapper;

-  private final SortedMap<String, Method> mangledNamesToImplementations;
-
-  private final Set<String> singleJsoImplTypes;
-
    /**
     * Creates a new {...@link HostedModeClassRewriter} for a specified set of
     * subclasses of JavaScriptObject.
@@ -137,9 +162,8 @@
     * @param mapper maps methods to the class in which they are declared
     */
    public HostedModeClassRewriter(Set<String> jsoSubtypes,
-      Map<String, List<String>> jsoSuperTypes,
-      SortedMap<String, Method> mangledNamesToImplementations,
-      Set<String> singleJsoImplTypes, InstanceMethodOracle mapper) {
+      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>>();
@@ -160,8 +184,7 @@
      this.jsoIntfDescs = Collections.unmodifiableSet(buildJsoIntfDescs);
      this.jsoImplDescs = Collections.unmodifiableSet(buildJsoImplDescs);
      this.jsoSuperDescs = Collections.unmodifiableMap(buildJsoSuperDescs);
-    this.mangledNamesToImplementations =  
Collections.unmodifiableSortedMap(mangledNamesToImplementations);
-    this.singleJsoImplTypes =  
Collections.unmodifiableSet(singleJsoImplTypes);
+    this.jsoData = jsoData;
      this.mapper = mapper;
    }

@@ -202,14 +225,12 @@
      // v = new CheckClassAdapter(v);
      // v = new TraceClassVisitor(v, new PrintWriter(System.out));

-    v = new RewriteSingleJsoImplDispatches(v, typeOracle,  
singleJsoImplTypes,
-        mangledNamesToImplementations);
+    v = new RewriteSingleJsoImplDispatches(v, typeOracle, jsoData);

      v = new RewriteRefsToJsoClasses(v, jsoIntfDescs, mapper);

      if (jsoImplDescs.contains(desc)) {
-      v = WriteJsoImpl.create(v, desc, jsoIntfDescs, mapper,
-          mangledNamesToImplementations);
+      v = WriteJsoImpl.create(v, desc, jsoIntfDescs, mapper, jsoData);
      }

      v = new RewriteJsniMethods(v, anonymousClassMap);
=======================================
---  
/trunk/dev/core/src/com/google/gwt/dev/shell/rewrite/RewriteSingleJsoImplDispatches.java
         
Tue Oct 13 16:57:19 2009
+++  
/trunk/dev/core/src/com/google/gwt/dev/shell/rewrite/RewriteSingleJsoImplDispatches.java
         
Sun Oct 18 11:48:45 2009
@@ -24,6 +24,7 @@
  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;

@@ -67,11 +68,10 @@
      public void visitMethodInsn(int opcode, String owner, String name,
          String desc) {
        if (opcode == Opcodes.INVOKEINTERFACE) {
-        if (singleJsoImplTypes.contains(owner)) {
+        if (jsoData.getSingleJsoIntfTypes().contains(owner)) {
            // Simple case; referring directly to a SingleJso interface.
            name = owner.replace('/', '_') + "_" + name;
-          assert  
mangledNamesToImplementations.containsKey(name) : "Missing "
-              + name;
+          assert jsoData.getMangledNames().contains(name) : "Missing " +  
name;

          } else {
            /*
@@ -86,7 +86,7 @@
             * void bar() { ((IB) object).foo(); }
             */
            for (String intf : computeAllInterfaces(owner)) {
-            if (singleJsoImplTypes.contains(intf)) {
+            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
@@ -95,7 +95,7 @@
                 * is undefined.
                 */
                String maybeMangled = intf.replace('/', '_') + "_" + name;
-              Method method =  
mangledNamesToImplementations.get(maybeMangled);
+              Method method = jsoData.getImplementation(maybeMangled);
                if (method != null) {
                  /*
                   * Found a method with the right name, but we need to  
check the
@@ -127,17 +127,14 @@
    private final Set<String> implementedMethods = new HashSet<String>();
    private boolean inSingleJsoImplInterfaceType;
    private Map<String, Set<String>> intfNamesToAllInterfaces =  
Maps.create();
-  private final SortedMap<String, Method> mangledNamesToImplementations;
-  private final Set<String> singleJsoImplTypes;
+  private final SingleJsoImplData jsoData;
    private final TypeOracle typeOracle;

    public RewriteSingleJsoImplDispatches(ClassVisitor v, TypeOracle  
typeOracle,
-      Set<String> singleJsoImplTypes,
-      SortedMap<String, Method> mangledNamesToImplementations) {
+      SingleJsoImplData jsoData) {
      super(v);
      this.typeOracle = typeOracle;
-    this.singleJsoImplTypes =  
Collections.unmodifiableSet(singleJsoImplTypes);
-    this.mangledNamesToImplementations =  
Collections.unmodifiableSortedMap(mangledNamesToImplementations);
+    this.jsoData = jsoData;
    }

    @Override
@@ -156,7 +153,8 @@
      }

      currentTypeName = name;
-    inSingleJsoImplInterfaceType = singleJsoImplTypes.contains(name);
+    inSingleJsoImplInterfaceType =  
jsoData.getSingleJsoIntfTypes().contains(
+        name);

      /*
       * Implements objective #2: non-JSO types that implement a  
SingleJsoImpl
@@ -166,7 +164,7 @@
       */
      if (interfaces != null && (access & Opcodes.ACC_INTERFACE) == 0) {
        Set<String> toStub = computeAllInterfaces(interfaces);
-      toStub.retainAll(singleJsoImplTypes);
+      toStub.retainAll(jsoData.getSingleJsoIntfTypes());

        for (String stubIntr : toStub) {
          writeTrampoline(stubIntr);
@@ -261,8 +259,11 @@
      String name = typeName.replace('/', '_');
      String prefix = name + "_";
      String suffix = name + "`";
-    SortedMap<String, Method> toReturn = new TreeMap<String, Method>(
-        mangledNamesToImplementations.subMap(prefix, suffix));
+    SortedMap<String, Method> toReturn = new TreeMap<String, Method>();
+
+    for (String mangledName : jsoData.getMangledNames().subSet(prefix,  
suffix)) {
+      toReturn.put(mangledName, jsoData.getImplementation(mangledName));
+    }
      toReturn.keySet().removeAll(implementedMethods);
      return toReturn;
    }
=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/shell/rewrite/WriteJsoImpl.java      
 
Mon Aug 31 14:02:45 2009
+++ /trunk/dev/core/src/com/google/gwt/dev/shell/rewrite/WriteJsoImpl.java      
 
Sun Oct 18 11:48:45 2009
@@ -23,9 +23,9 @@
  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.Map;
  import java.util.Set;

  /**
@@ -58,13 +58,13 @@
       * <code>JavaScriptObject</code> and all subclasses.
       */
      private final Set<String> jsoDescriptors;
-    private final Map<String, Method> methodsToImplement;
+    private final SingleJsoImplData jsoData;

      public ForJsoDollar(ClassVisitor cv, Set<String> jsoDescriptors,
-        InstanceMethodOracle mapper, Map<String, Method>  
methodsToImplement) {
+        InstanceMethodOracle mapper, SingleJsoImplData jsoData) {
        super(cv, mapper);
        this.jsoDescriptors = jsoDescriptors;
-      this.methodsToImplement = methodsToImplement;
+      this.jsoData = jsoData;
      }

      @Override
@@ -89,8 +89,9 @@
        }

        // Implement the trampoline methods
-      for (Map.Entry<String, Method> entry :  
methodsToImplement.entrySet()) {
-        writeTrampoline(entry.getKey(), entry.getValue());
+      for (String mangledName : jsoData.getMangledNames()) {
+        writeTrampoline(mangledName, jsoData.getDeclaration(mangledName),
+            jsoData.getImplementation(mangledName));
        }
      }

@@ -113,24 +114,31 @@
       * In Java, it might look like:
       *
       * <pre>
-     * public String com_google_Interface_someMethod(int a, double b) {
-     *   return com.google.MyJso$.someMethod$(this, a, b);
+     * 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);
+     *   }
       * }
       * </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 writeTrampoline(String mangledName, Method  
implementingMethod) {
+    private void writeTrampoline(String mangledName, Method  
interfaceMethod,
+        Method implementingMethod) {
+      assert implementingMethod.getArgumentTypes().length > 0;
+
        /*
-       * We derive the local descriptor by simply removing the first  
argument
-       * from the static method we want to call.
+       * The local descriptor is the same as the descriptor from the  
abstract
+       * method in the interface.
         */
-      assert implementingMethod.getArgumentTypes().length > 0;
-      String localDescriptor = "("
-          + implementingMethod.getDescriptor().substring(
-              1 +  
implementingMethod.getArgumentTypes()[0].getDescriptor().length());
+      String localDescriptor = interfaceMethod.getDescriptor();
        Method localMethod = new Method(mangledName, localDescriptor);

        /*
@@ -217,10 +225,10 @@
     */
    public static ClassVisitor create(ClassVisitor cv, String  
classDescriptor,
        Set<String> jsoDescriptors, InstanceMethodOracle mapper,
-      Map<String, Method> methodsToImplement) {
+      SingleJsoImplData singleJsoImplData) {

      if  
(classDescriptor.equals(HostedModeClassRewriter.JAVASCRIPTOBJECT_IMPL_DESC))  
{
-      return new ForJsoDollar(cv, jsoDescriptors, mapper,  
methodsToImplement);
+      return new ForJsoDollar(cv, jsoDescriptors, mapper,  
singleJsoImplData);
      } else {
        return new ForJsoInterface(cv, mapper);
      }
=======================================
---  
/trunk/user/test/com/google/gwt/dev/jjs/test/singlejso/TypeHierarchyTest.java   
 
Fri Sep 25 11:27:03 2009
+++  
/trunk/user/test/com/google/gwt/dev/jjs/test/singlejso/TypeHierarchyTest.java   
 
Sun Oct 18 11:48:45 2009
@@ -31,6 +31,12 @@
      int getLength();

      JavaScriptObject getObject(int i);
+
+    /**
+     * Used to test virtual override where the implementation has a  
narrower
+     * return type.
+     */
+    Wide wide();
    }

    /**
@@ -68,6 +74,15 @@
     */
    interface IDiamond2B extends IDiamond1 {
    }
+
+  /**
+   * Used for testing virtual overrides.
+   */
+  static class Narrow extends Wide {
+    public String toString() {
+      return "Narrow";
+    }
+  }

    /**
     * This is a base class that is used to test adding interfaces to a JSO  
via a
@@ -84,6 +99,10 @@
      public final native JavaScriptObject getObject(int i) /*-{
        return this[i];
      }-*/;
+
+    public final Narrow wide() {
+      return new Narrow();
+    }
    }

    /**
@@ -97,6 +116,12 @@
      protected PlainJsoWithInterface() {
      }
    }
+
+  /**
+   * Used for testing virtual overrides.
+   */
+  static class Wide {
+  }

    @Override
    public String getModuleName() {
@@ -151,5 +176,6 @@
      Arrayish array = PlainJsoWithInterface.create();
      assertEquals(0, array.getLength());
      assertNull(array.getObject(0));
+    assertEquals("Narrow", array.wide().toString());
    }
  }

--~--~---------~--~----~------------~-------~--~----~
http://groups.google.com/group/Google-Web-Toolkit-Contributors
-~----------~----~----~----~------~----~------~--~---

Reply via email to