[ductilej] push by samskivert - Reinstated some lenient method resolution (require only name and arity... on 2010-11-30 00:15 GMT

1 view
Skip to first unread message

duct...@googlecode.com

unread,
Nov 29, 2010, 7:16:18 PM11/29/10
to ductil...@googlegroups.com
Revision: ee79d8277f
Author: Michael Bayne <m...@samskivert.com>
Date: Mon Nov 29 16:15:31 2010
Log: Reinstated some lenient method resolution (require only name and arity
match).
Currently qualified methods (foo.bar(), Foo.bar()) and unqualified methods
(bar()) are handled, but constructors are not. Other loose ends exist in the
lenient method resolution (particularly with ambiguous overloads), but what
we've got here will get us far.
http://code.google.com/p/ductilej/source/detail?r=ee79d8277f

Modified:
/src/main/java/org/ductilej/detyper/Resolver.java
/src/test/java/org/ductilej/dtests/FindStaticMethodTest.java

=======================================
--- /src/main/java/org/ductilej/detyper/Resolver.java Sat Nov 27 12:27:58
2010
+++ /src/main/java/org/ductilej/detyper/Resolver.java Mon Nov 29 16:15:31
2010
@@ -10,6 +10,7 @@
import com.sun.tools.javac.code.BoundKind;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.code.Kinds;
+import com.sun.tools.javac.code.Scope;
import com.sun.tools.javac.code.Symbol.*;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.code.Symtab;
@@ -149,7 +150,7 @@
mi.msym = invoke(env, Backdoor.resolveConstructor, _resolve,
clazz.pos(),
aenv, site, mi.atypes, mi.tatypes);
mi.varArgs = Backdoor.varArgs.get(aenv.info);
- if (mi.msym.kind >= Kinds.ERR) {
+ if (!mi.isValid()) {
Debug.log("Unable to resolve ctor", "clazz", clazz, "args",
args, "targrs", typeargs);
}
// Debug.temp("Asked javac to resolve ctor " + clazz + " got " +
mi.msym);
@@ -188,8 +189,10 @@
// Debug.temp("Resolving " + mname + "<" + mi.tatypes
+ ">(" + mi.atypes + ")");
mi.msym = invoke(env, Backdoor.resolveConstructor,
_resolve, mexpr.pos(),
aenv, mi.site, mi.atypes, mi.tatypes);
- // TODO: if the type-correct resolver failed, fall back to
looser resolution
mi.varArgs = Backdoor.varArgs.get(aenv.info);
+ if (!mi.isValid()) {
+ // TODO: the type-correct resolver failed, fall back
to looser resolution
+ }

} else {
// Debug.temp("Resolving " + mname + "<" + mi.tatypes
+ ">(" + mi.atypes + ")");
@@ -210,6 +213,10 @@
steps = steps.tail;
}
}
+ if (!mi.isValid()) {
+ mi.msym = findFunLenient(aenv, mname, mi.atypes,
mi.tatypes);
+ mi.varArgs = Backdoor.varArgs.get(aenv.info);
+ }
// TODO: if the type-correct resolver failed, fall back to
looser resolution
}
if (mi.msym.kind >= Kinds.ERR) {
@@ -271,7 +278,13 @@
steps = steps.tail;
}
}
- if (mi.msym.kind >= Kinds.ERR) {
+ if (!mi.isValid()) {
+ // the type-correct resolver failed, fall back to looser
resolution
+ mi.msym = findMethodLenient(
+ aenv, mi.site, mname, mi.atypes, mi.tatypes,
mi.site.tsym.type);
+ mi.varArgs = Backdoor.varArgs.get(aenv.info);
+ }
+ if (!mi.isValid()) {
Debug.log("Unable to resolve method", "expr",
mexpr, "site", mi.site);
}
// Debug.temp("Asked javac to resolve method " + mexpr + "
got " + mi.msym);
@@ -393,7 +406,7 @@
final Env<DetypeContext> fenv = env;
JCMethodInvocation app = (JCMethodInvocation)expr;
MethInfo mi = resolveMethod(env, app);
- if (mi.msym.kind >= Kinds.ERR) {
+ if (!mi.isValid()) {
return null;
}

@@ -804,6 +817,158 @@

return rtype;
}
+
+ protected Symbol findFunLenient (Env<AttrContext> env, Name name,
+ List<Type> atypes, List<Type> tatypes)
+ {
+ Symbol bestSoFar = ABSENT_MTH;
+
+ // search the method in question in this class, then its enclosing
class, and so forth up
+ // the chain of enclosing classes
+ boolean staticOnly = false;
+ for (Env<AttrContext> env1 = env; env1.outer != null; env1 =
env1.outer) {
+ // TODO
+ // if (isStatic(env1)) staticOnly = true;
+
+ // call findMethodLenient for this class, which searchs up the
supertype chain
+ Type site = env1.enclClass.sym.type;
+ Symbol sym = findMethodLenient(env1, site, name, atypes,
tatypes, site.tsym.type);
+ // if we found a concrete method, call it
+ if (sym.exists()) {
+ // TODO: should we just skip over non-static methods when
seen in a static context?
+ // if (staticOnly && sym.kind == Kinds.MTH &&
sym.owner.kind == Kinds.TYP &&
+ // (sym.flags() & Flags.STATIC) == 0) return new
StaticError(sym);
+ // else
+ return sym;
+ } else if (sym.kind < bestSoFar.kind) {
+ bestSoFar = sym;
+ }
+ if ((env1.enclClass.sym.flags() & Flags.STATIC) != 0)
staticOnly = true;
+ env1 = env1.outer;
+ }
+
+ // look for a match in the predef class (TODO: I don't think any
methods are entered into
+ // the predef class, so I'm not sure why javac does this)
+ Type site = _syms.predefClass.type;
+ Symbol sym = findMethodLenient(env, site, name, atypes, tatypes,
site.tsym.type);
+ if (sym.exists()) {
+ return sym;
+ }
+
+ // check for a named-imported method that matches this name
+ for (Scope.Entry e = env.toplevel.namedImportScope.lookup(name);
+ e.scope != null; e = e.next()) {
+ sym = e.sym;
+ Type origin = e.getOrigin().owner.type;
+ if (sym.kind == Kinds.MTH) {
+ if (e.sym.owner.type != origin) {
+ sym = sym.clone(e.getOrigin().owner);
+ }
+ // TODO: for now we're ignoring access controls; we could
in theory choose
+ // accessible methods over inaccessible methods in the
event of collision
+ // if (!_resolve.isAccessible(aenv, origin, sym)) {
+ // sym = new AccessError(env, origin, sym);
+ // }
+ bestSoFar = selectBest(env, origin, atypes, tatypes, sym,
bestSoFar);
+ }
+ }
+ if (bestSoFar.exists()) {
+ return bestSoFar;
+ }
+
+ // check for a star-imported method that matches this name
+ for (Scope.Entry e = env.toplevel.starImportScope.lookup(name);
+ e.scope != null; e = e.next()) {
+ sym = e.sym;
+ Type origin = e.getOrigin().owner.type;
+ if (sym.kind == Kinds.MTH) {
+ if (e.sym.owner.type != origin) {
+ sym = sym.clone(e.getOrigin().owner);
+ }
+ // TODO: for now we're ignoring access controls; we could
in theory choose
+ // accessible methods over inaccessible methods in the
event of collision
+ // if (!_resolve.isAccessible(aenv, origin, sym)) {
+ // sym = new AccessError(env, origin, sym);
+ // }
+ bestSoFar = selectBest(env, origin, atypes, tatypes, sym,
bestSoFar);
+ }
+ }
+
+ return bestSoFar;
+ }
+
+ protected Symbol findMethodLenient (Env<AttrContext> env, Type site,
Name name,
+ List<Type> atypes, List<Type>
tatypes, Type intype)
+ {
+ // TODO: handle varargs; first search for non-varargs matches,
then varargs matches
+
+ // consider all supertypes of the type in which we're seeking the
method, from nearest
+ // supertype to most distant (i.e. Object)
+ Symbol bestSoFar = ABSENT_MTH;
+ boolean checkifcs = true;
+ for (Type ct = intype; ct.tag == TypeTags.CLASS || ct.tag ==
TypeTags.TYPEVAR;
+ ct = _types.supertype(ct)) {
+ // if the candidate type is a type variable, erase it to its
upper bound
+ while (ct.tag == TypeTags.TYPEVAR) {
+ ct = ct.getUpperBound();
+ }
+
+ // if the candidate type is an abstract class, interface or
enum, don't check its
+ // interfaces for the method (TODO: why not?)
+ ClassSymbol c = (ClassSymbol)ct.tsym;
+ if ((c.flags() & (Flags.ABSTRACT | Flags.INTERFACE |
Flags.ENUM)) == 0) {
+ checkifcs = false;
+ }
+
+ // now check all members of the candidate type which have the
sought name
+ for (Scope.Entry e = c.members().lookup(name); e.scope !=
null; e = e.next()) {
+ // if the member is a method, and is non-synthetic, check
whether it's better than
+ // our current best match, and if so, use it as our new
best match
+ if (e.sym.kind == Kinds.MTH && (e.sym.flags_field &
Flags.SYNTHETIC) == 0) {
+ MethodSymbol msym = (MethodSymbol)e.sym;
+ if (msym.params.length() == atypes.length()) {
+ bestSoFar = selectBest(env, site, atypes, tatypes,
msym, bestSoFar);
+ }
+ }
+ }
+ //- System.out.println(" - " + bestSoFar);
+
+ // TODO: recurse over interfaces, if appropriate
+ // if (checkifcs) {
+ // Symbol concrete = methodNotFound;
+ // if ((bestSoFar.flags() & 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, bestSoFar,
+ // allowBoxing, useVarargs,
operator);
+ // }
+ // if (concrete != bestSoFar &&
+ // concrete.kind < ERR && bestSoFar.kind < ERR &&
+ // types.isSubSignature(concrete.type, bestSoFar.type))
+ // bestSoFar = concrete;
+ // }
+ }
+
+ return bestSoFar;
+ }
+
+ protected Symbol selectBest (Env<AttrContext> env, Type site,
List<Type> atypes,
+ List<Type> tatypes, Symbol candidate,
Symbol bestSoFar)
+ {
+ if (bestSoFar == AMBIGUOUS) {
+ return bestSoFar;
+ } else if (candidate.kind < bestSoFar.kind) {
+ return candidate;
+ } else {
+ // check whether candidate is overridden by bestSoFar,
otherwise return AMBIGUOUS
+ Debug.warn("NOTE: need to compare '" + bestSoFar + " with '" +
candidate + "'");
+ return bestSoFar;
+ }
+ }

protected Type numericPromote (Env<DetypeContext> env, JCTree arg)
{
@@ -894,5 +1059,10 @@
@Override public <R, P> R accept(ElementVisitor<R, P> v, P p) {
throw new AssertionError();
}
+ };
+ protected static final Symbol AMBIGUOUS = new Symbol(Kinds.AMBIGUOUS,
0, null, null, null) {
+ @Override public <R, P> R accept(ElementVisitor<R, P> v, P p) {
+ throw new AssertionError();
+ }
};
}
=======================================
--- /src/test/java/org/ductilej/dtests/FindStaticMethodTest.java Mon Nov 29
15:08:51 2010
+++ /src/test/java/org/ductilej/dtests/FindStaticMethodTest.java Mon Nov 29
16:15:31 2010
@@ -33,7 +33,7 @@
assertEquals("test!", rv);
// TODO: the following breaks because dbGet() fails to resolve,
and thus we fail to resolve
// the correct assertEquals()
- // assertEquals("test!", dbGet(new TestDatabase(), "test"));
+ assertEquals("test!", dbGet(new TestDatabase(), "test"));
}

@Test public void testStaticFinding ()

Reply all
Reply to author
Forward
0 new messages