[JDK-8209078] Unable to call default interface methods from named module
Alan Bateman
Alan.Bateman at oracle.com
Sun Sep 16 09:15:28 UTC 2018
On 14/09/2018 20:34, Johannes Kuhn wrote:
> :
>
> 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.
The common case will be a public interface in an exported package in
which case the proxy will be generated in the unnamed module of the
specified class loader. This means you can create a full-power lookup on
the proxy class with:
Class<?> proxyClass = proxy.getClass();
this.getClass().getModule().addReads(proxyClass.getModule());
Lookup l = MethodHandles.privateLookupIn(proxyClass,
MethodHandles.lookup());
If you invoke findSpecial on this lookup then I would expect it should work.
There can be cases where the proxy is encapsulated (because the
interfaces are not public or not in exported packages) but I assume you
don't need to be concerned with those for now.
> :
>
>
> 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.
>
Qualified exports complicated this, as does readability. JDK-8173978 [1]
tracks the need to support checking the intersection.
-Alan
[1] https://bugs.openjdk.java.net/browse/JDK-8173978
More information about the core-libs-dev
mailing list