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



Reply via email to