[JDK-8209078] Unable to call default interface methods from named module
Johannes Kuhn
info at j-kuhn.de
Fri Sep 14 19:34:10 UTC 2018
Hi everyone,
I recently reported JDK-8209078 "Unable to call default method from
interface in another module from named module" [1].
It is possible to call any default interface method directly using
MethodHandles.lookup().findSpecial(Interf.class, "defaultMethodName",
..., Intef.class).invoke(...);
This only works if the caller is in an unnamed module or Interf is in
the same module as the caller.
I now want to share some thoughts about this feature, and an idea how it
could be fixed, but this probably requires further discussion.
1. Use Case:
The most obvious use case is to to "implement" default methods in
instances created by j.l.r.Proxy. An implementation of
j.l.r.InvocationHandler that handles default methods could look like this:
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if (method.isDefault()) {
MethodType mt = methodType(method.getReturnType(),
method.getParameterTypes());
Class<?> declaringClass = method.getDeclaringClass();
Lookup l = MethodHandles.lookup();
MethodHandle mh = l.findSpecial(declaringClass,
method.getName(), mt, declaringClass)
.asFixedArity()
.bindTo(proxy);
return mh.invokeWithArguments(args);
}
// Handle the other methods
return null;
}
Such a feature is very valuable so the InvocationHandler does not break
if an interface is evolved by adding new default methods.
2. Documentation:
When I faced the problem above, I did a search on how to invoke default
methods on a proxy.
The above code was inspired by this mail [2] from John Rose, where he
mentions JDK-8130227 "JEP 274: Enhanced Method Handles" [3][4].
This is the only official documentation I could find on this feature.
Indeed, even the Javadoc for findSpecial [5] states that my code above
should not successfully run:
> Before method resolution, if the explicitly specified caller class is
> not identical with the lookup class, or if this lookup object does not
> have private access privileges, the access fails.
The specified caller class in the example above is the interface itself,
not the lookup class.
The documentation has to be at least changed to reflect this feature.
3. Cause of the discrepancy between lookup class in named module vs
unnamed module
As Mandy Chung observed:
> IAE is thrown because the implementation calls Lookup::in teleporting
> to a Lookup of the special class which lost all access since the
> caller class and the requested lookup class are in different modules.
This behavior is documented in the Lookup.in() [6]:
> * If the old lookup class is in a named module, and the new lookup
> class is in a different module M, then no members, not even public
> members in M's exported packages, will be accessible. The exception to
> this is when this lookup is publicLookup, in which case PUBLIC access
> is not lost.
> * If the old lookup class is in an unnamed module, and the new lookup
> class is a different module then MODULE access is lost.
This strikes me as a little bit odd, so I looked around for the
reasoning for this difference, and found an old discussion on jigsaw-dev
[7]. I especially found this mail from Alan Bateman [8] interesting,
where he gave a reasoning for this:
> Preserved but perhaps with the (initially surprising) consequence that
> all access is lost when m(LC) is a named module and m(A) is a
> different module. This arises because the two modules may read very
> different sets of modules, the intersection cannot be expressed via a
> lookup class + modes.
While I agree that intersection might not be expressed via lookup class
+ modes, I don't think that it is necessary to express that. Instead
don't allow any lookup on members in a different module if the MODULE
flag is not set.
But this is only a suggestion. It's up to you to decide how to deal with
this.
Please keep up the good work you are doing, and I hope this mail will
help you fixing this bug.
Thanks for your time,
Johannes Kuhn
[1]: https://bugs.openjdk.java.net/browse/JDK-8209078
[2]:
http://mail.openjdk.java.net/pipermail/jigsaw-dev/2017-January/010741.html
[3]: https://bugs.openjdk.java.net/browse/JDK-8130227
[4]: http://openjdk.java.net/jeps/274
[5]:
https://docs.oracle.com/javase/10/docs/api/java/lang/invoke/MethodHandles.Lookup.html#findSpecial(java.lang.Class,java.lang.String,java.lang.invoke.MethodType,java.lang.Class)
[6]:
https://docs.oracle.com/javase/10/docs/api/java/lang/invoke/MethodHandles.Lookup.html#in(java.lang.Class)
[7]:
http://mail.openjdk.java.net/pipermail/jigsaw-dev/2015-December/005751.html
[8]:
http://mail.openjdk.java.net/pipermail/jigsaw-dev/2015-December/005768.html
More information about the core-libs-dev
mailing list