When tightening method calls, use a more reliable technique to
determine what methods a given method overrides.  Use the
override chains determined when JTypeOracle is first built,
rather than trying to compare type signatures of methods.

Review by: scottb


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@3534 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
index ba92608..0d3e6b7 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
@@ -135,33 +135,6 @@
     return traceMethods.size() > 0;
   }
 
-  public static boolean methodsDoMatch(JMethod method1, JMethod method2) {
-    // static methods cannot match each other
-    if (method1.isStatic() || method2.isStatic()) {
-      return false;
-    }
-
-    // names must be identical
-    if (!method1.getName().equals(method2.getName())) {
-      return false;
-    }
-
-    // original parameter types must be identical
-    List<JType> params1 = method1.getOriginalParamTypes();
-    List<JType> params2 = method2.getOriginalParamTypes();
-    int params1size = params1.size();
-    if (params1size != params2.size()) {
-      return false;
-    }
-
-    for (int i = 0; i < params1size; ++i) {
-      if (params1.get(i) != params2.get(i)) {
-        return false;
-      }
-    }
-    return true;
-  }
-
   private static String dotify(char[][] name) {
     StringBuffer result = new StringBuffer();
     for (int i = 0; i < name.length; ++i) {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java
index c822c99..b7774a2 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java
@@ -137,6 +137,39 @@
     }
   }
 
+  /**
+   * Compare two methods based on name and original argument types
+   * {@link JMethod#getOriginalParamTypes()}. Note that nothing special is done
+   * here regarding methods with type parameters in their argument lists. The
+   * caller must be careful that this level of matching is sufficient.
+   */
+  private static boolean methodsDoMatch(JMethod method1, JMethod method2) {
+    // static methods cannot match each other
+    if (method1.isStatic() || method2.isStatic()) {
+      return false;
+    }
+
+    // names must be identical
+    if (!method1.getName().equals(method2.getName())) {
+      return false;
+    }
+
+    // original parameter types must be identical
+    List<JType> params1 = method1.getOriginalParamTypes();
+    List<JType> params2 = method2.getOriginalParamTypes();
+    int params1size = params1.size();
+    if (params1size != params2.size()) {
+      return false;
+    }
+
+    for (int i = 0; i < params1size; ++i) {
+      if (params1.get(i) != params2.get(i)) {
+        return false;
+      }
+    }
+    return true;
+  }
+
   private final Map<JInterfaceType, Set<JClassType>> couldBeImplementedMap = new IdentityHashMap<JInterfaceType, Set<JClassType>>();
 
   private final Map<JClassType, Set<JInterfaceType>> couldImplementMap = new IdentityHashMap<JClassType, Set<JInterfaceType>>();
@@ -573,7 +606,7 @@
   private void computeVirtualUpRefs(JClassType type, JInterfaceType intf) {
     outer : for (JMethod intfMethod : intf.methods) {
       for (JMethod classMethod : type.methods) {
-        if (JProgram.methodsDoMatch(intfMethod, classMethod)) {
+        if (methodsDoMatch(intfMethod, classMethod)) {
           // this class directly implements the interface method
           continue outer;
         }
@@ -583,7 +616,7 @@
       // if any super classes do, create a virtual up ref
       for (JClassType superType = type.extnds; superType != javaLangObject; superType = superType.extnds) {
         for (JMethod superMethod : superType.methods) {
-          if (JProgram.methodsDoMatch(intfMethod, superMethod)) {
+          if (methodsDoMatch(intfMethod, superMethod)) {
             // this super class directly implements the interface method
             // create a virtual up ref
 
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/MethodCallTightener.java b/dev/core/src/com/google/gwt/dev/jjs/impl/MethodCallTightener.java
index df16a0e..3a33dcd 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/MethodCallTightener.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/MethodCallTightener.java
@@ -44,7 +44,7 @@
    */
   public class MethodCallTighteningVisitor extends JModVisitor {
 
-    // @Override
+    @Override
     public void endVisit(JMethodCall x, Context ctx) {
       JMethod method = x.getTarget();
       JExpression instance = x.getInstance();
@@ -86,7 +86,7 @@
           && type != enclosingType; type = type.extnds) {
         for (int i = 0; i < type.methods.size(); ++i) {
           JMethod methodIt = type.methods.get(i);
-          if (JProgram.methodsDoMatch(method, methodIt)) {
+          if (methodOverrides(methodIt, method)) {
             foundMethod = methodIt;
             break outer;
           }
@@ -106,6 +106,31 @@
       call.getArgs().addAll(x.getArgs());
       ctx.replaceMe(call);
     }
+
+    /**
+     * Check whether <code>subMethod</code> overrides <code>supMethod</code>.
+     * For the purposes of this method, indirect overrides are considered
+     * overrides. For example, if method A.m overrides B.m, and B.m overrides
+     * C.m, then A.m is considered to override C.m. Additionally, implementing
+     * an interface is considered
+     * <q>overriding</q>
+     * for the purposes of this method.
+     * 
+     */
+    private boolean methodOverrides(JMethod subMethod, JMethod supMethod) {
+      if (subMethod.params.size() != supMethod.params.size()) {
+        // short cut: check the number of parameters
+        return false;
+      }
+
+      if (!subMethod.getName().equals(supMethod.getName())) {
+        // short cut: check the method names
+        return false;
+      }
+
+      // long way: get all overrides and see if supMethod is included
+      return program.typeOracle.getAllOverrides(subMethod).contains(supMethod);
+    }
   }
 
   public static boolean exec(JProgram program) {