[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