Author: [email protected]
Date: Mon Apr  6 12:48:03 2009
New Revision: 5191

Modified:
     
trunk/dev/core/src/com/google/gwt/core/ext/soyc/impl/StandardMethodMember.java
    trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java
    trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
    trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java
    trunk/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java
    trunk/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
    trunk/dev/core/src/com/google/gwt/dev/jjs/impl/MakeCallsStatic.java
    trunk/user/test/com/google/gwt/dev/jjs/test/CompilerTest.java

Log:
Adds more bridge methods so as to simplify and make more robust
virtual-method dispatch in the face of tricky generics cases.

Fixes issue 3517.

Patch by: scottb
Review by: spoon



Modified:  
trunk/dev/core/src/com/google/gwt/core/ext/soyc/impl/StandardMethodMember.java
==============================================================================
---  
trunk/dev/core/src/com/google/gwt/core/ext/soyc/impl/StandardMethodMember.java  
 
(original)
+++  
trunk/dev/core/src/com/google/gwt/core/ext/soyc/impl/StandardMethodMember.java  
 
Mon Apr  6 12:48:03 2009
@@ -51,6 +51,7 @@
        sb.append(type.getJsniSignatureName());
      }
      sb.append(")");
+    sb.append(method.getOriginalReturnType().getJsniSignatureName());
      this.sourceName = sb.toString();

      SortedSet<String> aliases = new TreeSet<String>();

Modified: trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java
==============================================================================
--- trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java  (original)
+++ trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java  Mon Apr  6  
12:48:03 2009
@@ -47,6 +47,7 @@
    private final boolean isStatic;
    private final String name;
    private List<JType> originalParamTypes;
+  private JType originalReturnType;

    /**
     * References to any methods which this method overrides. This should be  
an
@@ -102,7 +103,7 @@
      for (JParameter param : params) {
        paramTypes.add(param.getType());
      }
-    setOriginalParamTypes(paramTypes);
+    setOriginalTypes(returnType, paramTypes);
    }

    public JAbstractMethodBody getBody() {
@@ -124,6 +125,10 @@
      return originalParamTypes;
    }

+  public JType getOriginalReturnType() {
+    return originalReturnType;
+  }
+
    /**
     * Returns the transitive closure of all the methods this method  
overrides.
     */
@@ -189,10 +194,11 @@
      isFinal = true;
    }

-  public void setOriginalParamTypes(List<JType> paramTypes) {
+  public void setOriginalTypes(JType returnType, List<JType> paramTypes) {
      if (originalParamTypes != null) {
        throw new InternalCompilerException("Param types already frozen");
      }
+    originalReturnType = returnType;
      originalParamTypes = Lists.normalize(paramTypes);

      // Determine if we should trace this method.

Modified: trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
==============================================================================
--- trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java (original)
+++ trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java Mon Apr  6  
12:48:03 2009
@@ -124,6 +124,7 @@
        sb.append(type.getJsniSignatureName());
      }
      sb.append(")");
+    sb.append(method.getOriginalReturnType().getJsniSignatureName());
      return sb.toString();
    }


Modified: trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java
==============================================================================
--- trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java       
(original)
+++ trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java      Mon Apr 
  
6 12:48:03 2009
@@ -156,6 +156,11 @@
        return false;
      }

+    // original return type must be identical
+    if (method1.getOriginalReturnType() !=  
method2.getOriginalReturnType()) {
+      return false;
+    }
+
      // original parameter types must be identical
      List<JType> params1 = method1.getOriginalParamTypes();
      List<JType> params2 = method2.getOriginalParamTypes();

Modified:  
trunk/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java
==============================================================================
--- trunk/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java  
(original)
+++ trunk/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java Mon  
Apr  6 12:48:03 2009
@@ -309,8 +309,8 @@
       * </p>
       */
      public void addBridgeMethods(SourceTypeBinding clazzBinding) {
-      if (clazzBinding.isInterface() || clazzBinding.isAbstract()) {
-        // Only add bridges in concrete classes, to simplify matters.
+      if (clazzBinding.isInterface()) {
+        // Only add bridges in classes, to simplify matters.
          return;
        }

@@ -322,7 +322,8 @@
         */
        if (clazzBinding.syntheticMethods() != null) {
          for (SyntheticMethodBinding synthmeth :  
clazzBinding.syntheticMethods()) {
-          if (synthmeth.purpose == SyntheticMethodBinding.BridgeMethod) {
+          if (synthmeth.purpose == SyntheticMethodBinding.BridgeMethod
+              && !synthmeth.isStatic()) {
              JMethod implmeth = (JMethod)  
typeMap.get(synthmeth.targetMethod);

              createBridgeMethod(clazz, synthmeth, implmeth);
@@ -1482,7 +1483,7 @@
        MethodBinding b = x.binding;
        JMethod method = (JMethod) typeMap.get(b);
        try {
-        if (b.isImplementing() || b.isOverriding()) {
+        if (!b.isStatic() && (b.isImplementing() || b.isOverriding())) {
            tryFindUpRefs(method, b);
          }

@@ -2030,20 +2031,6 @@
        }
      }

-    private boolean classHasMethodOverriding(JClassType clazz, JMethod  
over) {
-      for (JMethod meth : clazz.methods) {
-        if (meth.getOverrides().contains(over)) {
-          return true;
-        }
-      }
-
-      if (clazz.extnds != null && classHasMethodOverriding(clazz.extnds,  
over)) {
-        return true;
-      }
-
-      return false;
-    }
-
      /**
       * Create a bridge method. It calls a same-named method with the same
       * arguments, but with a different type signature.
@@ -2101,15 +2088,20 @@
        JMethodBody body = (JMethodBody) bridgeMethod.getBody();
        body.getBlock().addStmt(callOrReturn);

-      // add overrides, but only for interface methods that the class does  
not
-      // already override
+      // Add overrides.
        List<JMethod> overrides = new ArrayList<JMethod>();
        tryFindUpRefs(bridgeMethod, overrides);
+      assert !overrides.isEmpty();
        for (JMethod over : overrides) {
-        if (!classHasMethodOverriding(clazz, over)) {
-          bridgeMethod.addOverride(over);
-          bridgeMethod.addOverrides(over.getOverrides());
-        }
+        bridgeMethod.addOverride(over);
+        /*
+         * TODO(scottb): with a diamond-shape inheritance hierarchy, it  
may be
+         * possible to get dups in this way. Really, method.overrides  
should
+         * probably just be an IdentitySet to avoid having to check  
contains in
+         * various places. Left as a todo because I don't think dups is  
super
+         * harmful.
+         */
+        bridgeMethod.addOverrides(over.getOverrides());
        }
      }

@@ -2369,8 +2361,8 @@
       * expression. Beware that when autoboxing, the type of the expression  
is
       * not necessarily the same as the type of the box to be created. The  
JDT
       * figures out what the necessary conversion is, depending on the  
context
-     * the expression appears in, and stores it in  
<code>x.implicitConversion</code>,
-     * so extract it from there.
+     * the expression appears in, and stores it in
+     * <code>x.implicitConversion</code>, so extract it from there.
       */
      private JPrimitiveType implicitConversionTargetType(Expression x)
          throws InternalCompilerException {
@@ -2557,6 +2549,8 @@
       * overrides/implements.
       */
      private void tryFindUpRefs(JMethod method, MethodBinding binding) {
+      // Should never get a parameterized instance here.
+      assert binding == binding.original();
        tryFindUpRefsRecursive(method, binding, binding.declaringClass);
      }

@@ -2595,11 +2589,18 @@
       */
      private void tryFindUpRefsRecursive(JMethod method, MethodBinding  
binding,
          ReferenceBinding searchThisType) {
+      /*
+       * Always look for uprefs in the original, so we can correctly  
compare
+       * erased signatures. The general design for uprefs is to model what  
the
+       * JVM does in terms of matching up overrides based on binary match.
+       */
+      searchThisType = (ReferenceBinding) searchThisType.original();

        // See if this class has any uprefs, unless this class is myself
        if (binding.declaringClass != searchThisType) {
          for (MethodBinding tryMethod :  
searchThisType.getMethods(binding.selector)) {
-          if (binding.areParameterErasuresEqual(tryMethod)) {
+          if (binding.returnType.erasure() ==  
tryMethod.returnType.erasure()
+              && binding.areParameterErasuresEqual(tryMethod)) {
              JMethod upRef = (JMethod) typeMap.get(tryMethod);
              if (!method.getOverrides().contains(upRef)) {
                method.addOverride(upRef);
@@ -2790,6 +2791,8 @@
                  if (method.getName().equals(methodName)) {
                    String sig = JProgram.getJsniSig(method);
                    if (sig.equals(jsniSig)) {
+                    return method;
+                  } else if (sig.startsWith(jsniSig) &&  
jsniSig.endsWith(")")) {
                      return method;
                    } else {
                      almostMatches.add(sig);

Modified:  
trunk/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
==============================================================================
---  
trunk/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java       
 
(original)
+++  
trunk/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java       
 
Mon Apr  6 12:48:03 2009
@@ -405,10 +405,12 @@

        if (x instanceof JMethod) {
          sb.append('(');
-        for (JType t : ((JMethod) x).getOriginalParamTypes()) {
+        JMethod method = ((JMethod) x);
+        for (JType t : method.getOriginalParamTypes()) {
            sb.append(t.getJsniSignatureName());
          }
          sb.append(')');
+        sb.append(method.getOriginalReturnType().getJsniSignatureName());
        }

        SymbolData symbolData = StandardSymbolData.forMember(
@@ -1958,6 +1960,7 @@
        JType type = x.getOriginalParamTypes().get(i);
        s += type.getJavahSignatureName();
      }
+    s += x.getOriginalReturnType().getJavahSignatureName();
      return s;
    }

@@ -1980,6 +1983,7 @@
        JType type = x.getOriginalParamTypes().get(i);
        s += type.getJavahSignatureName();
      }
+    s += x.getOriginalReturnType().getJavahSignatureName();
      return s;
    }


Modified:  
trunk/dev/core/src/com/google/gwt/dev/jjs/impl/MakeCallsStatic.java
==============================================================================
--- trunk/dev/core/src/com/google/gwt/dev/jjs/impl/MakeCallsStatic.java  
(original)
+++ trunk/dev/core/src/com/google/gwt/dev/jjs/impl/MakeCallsStatic.java Mon  
Apr  6 12:48:03 2009
@@ -166,7 +166,7 @@
        List<JType> originalParamTypes = new ArrayList<JType>();
        originalParamTypes.add(enclosingType);
        originalParamTypes.addAll(x.getOriginalParamTypes());
-      newMethod.setOriginalParamTypes(originalParamTypes);
+      newMethod.setOriginalTypes(x.getOriginalReturnType(),  
originalParamTypes);

        // Move the body of the instance method to the static method
        JAbstractMethodBody movedBody = x.getBody();
@@ -230,6 +230,9 @@
     */
    private class FindStaticDispatchSitesVisitor extends JVisitor {

+    private JMethod currentMethod;
+    private ControlFlowAnalyzer initiallyLive;
+
      @Override
      public void endVisit(JMethodCall x, Context ctx) {
        JMethod method = x.getTarget();
@@ -259,9 +262,36 @@
          // The target method was already pruned (TypeTightener will fix  
this).
          return;
        }
+
+      if (initiallyLive.getLiveFieldsAndMethods().contains(currentMethod)
+           
&& !initiallyLive.getLiveFieldsAndMethods().contains(x.getTarget())) {
+        /*
+         * Don't devirtualize calls from initial code to non-initial code.
+         */
+        return;
+      }

        // Let's do it!
        toBeMadeStatic.add(method);
+    }
+
+    @Override
+    public boolean visit(JMethod x, Context ctx) {
+      currentMethod = x;
+      return true;
+    }
+
+    @Override
+    public boolean visit(JProgram x, Context ctx) {
+      // TODO(spoon) factor out this computation of the initially live  
stuff to
+      // a method in CodeSplitter
+      initiallyLive = new ControlFlowAnalyzer(x);
+      for (JMethod entry : x.entryMethods.get(0)) {
+        initiallyLive.traverseFrom(entry);
+      }
+      initiallyLive.traverseFromClassLiteralFactories();
+
+      return true;
      }
    }


Modified: trunk/user/test/com/google/gwt/dev/jjs/test/CompilerTest.java
==============================================================================
--- trunk/user/test/com/google/gwt/dev/jjs/test/CompilerTest.java       
(original)
+++ trunk/user/test/com/google/gwt/dev/jjs/test/CompilerTest.java       Mon Apr 
  
6 12:48:03 2009
@@ -28,6 +28,10 @@
  @SuppressWarnings("unused")
  public class CompilerTest extends GWTTestCase {

+  interface MyMap {
+    Object get(String key);
+  }
+
    interface Silly { }

    interface SillyComparable<T extends Silly> extends Comparable<T> {
@@ -319,6 +323,26 @@
      }

      assertEquals(0, new MyFoo().compareTo(new MyFoo()));
+  }
+
+  /**
+   * Issue 3517. Sometimes JDT adds a bridge method when a subclass's  
method
+   * differs only by return type. In some versions of GWT, this has  
resulted in
+   * a bridge method overriding its own target, and eventually  
TypeTightener
+   * producing an infinite recursion.
+   */
+  public void testBridgeMethods4() {
+    abstract class MyMapAbstract<V> implements MyMap {
+      public String get(String key) {
+        return null;
+      }
+    }
+
+    final class MyMapImpl<V> extends MyMapAbstract<V> {
+    }
+
+    MyMapImpl<String> mmap = new MyMapImpl<String>();
+    assertNull(mmap.get("foo"));
    }

    public void testCastOptimizer() {

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

Reply via email to