changeset 14045:c66c0a81c4ab

7193913: Cleanup Resolve.findMethod Summary: Refactor method lookup logic in Resolve.findMethod Reviewed-by: jjg
author mcimadamore
date Tue, 25 Sep 2012 11:52:37 +0100
parents 0070e9c44020
children 8ef5d5b19998
files langtools/src/share/classes/com/sun/tools/javac/comp/Resolve.java
diffstat 1 files changed, 126 insertions(+), 45 deletions(-) [+]
line wrap: on
line diff
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Resolve.java	Mon Sep 24 14:04:34 2012 -0700
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Resolve.java	Tue Sep 25 11:52:37 2012 +0100
@@ -40,6 +40,7 @@
 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticType;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.EnumMap;
@@ -57,6 +58,7 @@
 import static com.sun.tools.javac.code.TypeTags.*;
 import static com.sun.tools.javac.comp.Resolve.MethodResolutionPhase.*;
 import static com.sun.tools.javac.tree.JCTree.Tag.*;
+import java.util.Iterator;
 
 /** Helper class for name resolution, used mostly by the attribution phase.
  *
@@ -1035,8 +1037,8 @@
                             return this;
                         else
                             return super.implementation(origin, types, checkResult);
-                    }
-                };
+                        }
+                    };
                 return result;
             }
             if (m1SignatureMoreSpecific) return m1;
@@ -1160,12 +1162,10 @@
                           argtypes,
                           typeargtypes,
                           site.tsym.type,
-                          true,
                           bestSoFar,
                           allowBoxing,
                           useVarargs,
-                          operator,
-                          new HashSet<TypeSymbol>());
+                          operator);
         reportVerboseResolutionDiagnostic(env.tree.pos(), name, site, argtypes, typeargtypes, bestSoFar);
         return bestSoFar;
     }
@@ -1176,56 +1176,137 @@
                               List<Type> argtypes,
                               List<Type> typeargtypes,
                               Type intype,
-                              boolean abstractok,
                               Symbol bestSoFar,
                               boolean allowBoxing,
                               boolean useVarargs,
-                              boolean operator,
-                              Set<TypeSymbol> seen) {
-        for (Type ct = intype; ct.tag == CLASS || ct.tag == TYPEVAR; ct = types.supertype(ct)) {
-            while (ct.tag == TYPEVAR)
-                ct = ct.getUpperBound();
-            ClassSymbol c = (ClassSymbol)ct.tsym;
-            if (!seen.add(c)) return bestSoFar;
-            if ((c.flags() & (ABSTRACT | INTERFACE | ENUM)) == 0)
-                abstractok = false;
-            for (Scope.Entry e = c.members().lookup(name);
-                 e.scope != null;
-                 e = e.next()) {
-                //- System.out.println(" e " + e.sym);
-                if (e.sym.kind == MTH &&
-                    (e.sym.flags_field & SYNTHETIC) == 0) {
-                    bestSoFar = selectBest(env, site, argtypes, typeargtypes,
-                                           e.sym, bestSoFar,
-                                           allowBoxing,
-                                           useVarargs,
-                                           operator);
+                              boolean operator) {
+        boolean abstractOk = true;
+        List<Type> itypes = List.nil();
+        for (TypeSymbol s : superclasses(intype)) {
+            bestSoFar = lookupMethod(env, site, name, argtypes, typeargtypes,
+                    s.members(), bestSoFar, allowBoxing, useVarargs, operator, true);
+            abstractOk &= excludeAbstractsFilter.accepts(s);
+            if (abstractOk) {
+                for (Type itype : types.interfaces(s.type)) {
+                    itypes = types.union(types.closure(itype), itypes);
                 }
             }
-            if (name == names.init)
-                break;
-            //- System.out.println(" - " + bestSoFar);
-            if (abstractok) {
-                Symbol concrete = methodNotFound;
-                if ((bestSoFar.flags() & ABSTRACT) == 0)
-                    concrete = bestSoFar;
-                for (List<Type> l = types.interfaces(c.type);
-                     l.nonEmpty();
-                     l = l.tail) {
-                    bestSoFar = findMethod(env, site, name, argtypes,
-                                           typeargtypes,
-                                           l.head, abstractok, bestSoFar,
-                                           allowBoxing, useVarargs, operator, seen);
-                }
-                if (concrete != bestSoFar &&
-                    concrete.kind < ERR  && bestSoFar.kind < ERR &&
-                    types.isSubSignature(concrete.type, bestSoFar.type))
-                    bestSoFar = concrete;
+            if (name == names.init) break;
+        }
+
+        Symbol concrete = bestSoFar.kind < ERR &&
+                (bestSoFar.flags() & ABSTRACT) == 0 ?
+                bestSoFar : methodNotFound;
+
+        if (name != names.init) {
+            //keep searching for abstract methods
+            for (Type itype : itypes) {
+                if (!itype.isInterface()) continue; //skip j.l.Object (included by Types.closure())
+                bestSoFar = lookupMethod(env, site, name, argtypes, typeargtypes,
+                    itype.tsym.members(), bestSoFar, allowBoxing, useVarargs, operator, true);
+                    if (concrete != bestSoFar &&
+                            concrete.kind < ERR  && bestSoFar.kind < ERR &&
+                            types.isSubSignature(concrete.type, bestSoFar.type)) {
+                        //this is an hack - as javac does not do full membership checks
+                        //most specific ends up comparing abstract methods that might have
+                        //been implemented by some concrete method in a subclass and,
+                        //because of raw override, it is possible for an abstract method
+                        //to be more specific than the concrete method - so we need
+                        //to explicitly call that out (see CR 6178365)
+                        bestSoFar = concrete;
+                    }
             }
         }
         return bestSoFar;
     }
 
+    /**
+     * Return an Iterable object to scan the superclasses of a given type.
+     * It's crucial that the scan is done lazily, as we don't want to accidentally
+     * access more supertypes than strictly needed (as this could trigger completion
+     * errors if some of the not-needed supertypes are missing/ill-formed).
+     */
+    Iterable<TypeSymbol> superclasses(final Type intype) {
+        return new Iterable<TypeSymbol>() {
+            public Iterator<TypeSymbol> iterator() {
+                return new Iterator<TypeSymbol>() {
+
+                    List<TypeSymbol> seen = List.nil();
+                    TypeSymbol currentSym = getSymbol(intype);
+
+                    public boolean hasNext() {
+                        return currentSym != null;
+                    }
+
+                    public TypeSymbol next() {
+                        TypeSymbol prevSym = currentSym;
+                        currentSym = getSymbol(types.supertype(currentSym.type));
+                        return prevSym;
+                    }
+
+                    public void remove() {
+                        throw new UnsupportedOperationException("Not supported yet.");
+                    }
+
+                    TypeSymbol getSymbol(Type intype) {
+                        if (intype.tag != CLASS &&
+                                intype.tag != TYPEVAR) {
+                            return null;
+                        }
+                        while (intype.tag == TYPEVAR)
+                            intype = intype.getUpperBound();
+                        if (seen.contains(intype.tsym)) {
+                            //degenerate case in which we have a circular
+                            //class hierarchy - because of ill-formed classfiles
+                            return null;
+                        }
+                        seen = seen.prepend(intype.tsym);
+                        return intype.tsym;
+                    }
+                };
+            }
+        };
+    }
+
+    /**
+     * We should not look for abstract methods if receiver is a concrete class
+     * (as concrete classes are expected to implement all abstracts coming
+     * from superinterfaces)
+     */
+    Filter<Symbol> excludeAbstractsFilter = new Filter<Symbol>() {
+        public boolean accepts(Symbol s) {
+            return (s.flags() & (ABSTRACT | INTERFACE | ENUM)) != 0;
+        }
+    };
+
+    /**
+     * Lookup a method with given name and argument types in a given scope
+     */
+    Symbol lookupMethod(Env<AttrContext> env,
+            Type site,
+            Name name,
+            List<Type> argtypes,
+            List<Type> typeargtypes,
+            Scope sc,
+            Symbol bestSoFar,
+            boolean allowBoxing,
+            boolean useVarargs,
+            boolean operator,
+            boolean abstractok) {
+        for (Symbol s : sc.getElementsByName(name, lookupFilter)) {
+            bestSoFar = selectBest(env, site, argtypes, typeargtypes, s,
+                    bestSoFar, allowBoxing, useVarargs, operator);
+        }
+        return bestSoFar;
+    }
+    //where
+        Filter<Symbol> lookupFilter = new Filter<Symbol>() {
+            public boolean accepts(Symbol s) {
+                return s.kind == MTH &&
+                        (s.flags() & SYNTHETIC) == 0;
+            }
+        };
+
     /** Find unqualified method matching given name, type and value arguments.
      *  @param env       The current environment.
      *  @param name      The method's name.