Java 11 vs 14 MethodHandle behavior change

Mandy Chung mandy.chung at oracle.com
Tue Apr 28 17:34:13 UTC 2020


Hi Simone,

On 4/28/20 5:42 AM, Simone Bordet wrote:
> Hi,
>
> in Jetty 10, we use method handles in the WebSocket implementation.
>
> We need to scan WebSocket EndPoint application classes (MyEndPoint)
> for annotated methods whose signature may vary and may take
> application classes, for example `@OnMessage void onMessage(Session s,
> MyString m)` where @OnMessage and Session are JSR 356 classes and
> MyEndPoint and MyString are application classes.
>
> It may be possible that the same web application is deployed twice,
> perhaps with different configurations.
>
> Therefore we have:
> * class "Impl" is the Jetty implementation class that creates
> MethodHandle.Lookup, loaded by serverClassLoader and in a named JPMS
> module
> * class MyEndPoint and MyString are in webapp1's WEB-INF/classes,
> loaded by webClassLoader1 in an unnamed JPMS module
> * class MyEndPoint and MyString are also in webapp2's WEB-INF/classes,
> loaded by webClassLoader2 in another unnamed JPMS module
>
> Class Impl does this:
> MethodHandles.publicLookup().in(endPointClass).findVirtual(...), where
> "endPointClass" is class MyEndPoint loaded by the web class loader
> (either webClassLoader1 or webClassLoader2).

endPointClass is in unnamed module and so it's unconditionally 
exported.  The public lookup should be able to find public members from 
it.    One thing to double check if endPointClass is publicly accessible?
> We must call .in() to separate the 2 web applications (failing to call
> .in() results in linkage errors).
>
> Calling publicLookup().in() works in Java 11-13.
>
> In Java 14, it does not work anymore.
> The reason seems to stem from the fixes for
> https://bugs.openjdk.java.net/browse/JDK-8173978.

Did you get any exception in 14?  Is it from findVirtual or from in?
> In particular, in Java 14 publicLookup() returns a Lookup with
> mode=UNCONDITIONAL (but not PUBLIC? Is this intended?)

Yes this is intentional.  The new public lookup returned has 
endPointClass as the lookup class (which should not have behavioral 
change).  The access modes are summarized in the javadoc:

https://download.java.net/java/early_access/jdk15/docs/api/java.base/java/lang/invoke/MethodHandles.Lookup.html#access-modes

However, there is no behavioral change in public lookup.  Any lookup 
object with UNCONDITIONAL access mode assume readability and also finds 
all public accessible members as in Java 9.

See also CSR JDK-8226916.

> and
> Lookup.lookupClassOrNull() returns Object.class rather than the class
> passed to .in() - like it was doing in Java 11.
>
> We have debugged and manually changed the return value of
> lookupClassOrNull() from Object.class to the endPoint class, and it
> works again.

The current implementation already does that.

         private Class<?> lookupClassOrNull() {
             if (allowedModes == TRUSTED) {
                 return null;
             }
             if (allowedModes == UNCONDITIONAL) {
                 // use Object as the caller to pass to VM doing resolution
                 return Object.class;
             }
             return lookupClass;
         }

What exactly have you changed?
>
> We have written a simple reproducer and been able to workaround the
> issue (we need to test it better in Jetty to confirm that the
> workaround for the simple reproducer also works in Jetty) by changing
> the Lookup from: publicLookup().in() to: lookup().dropLookupMode(<all
> apart PUBLIC>).in() and it also works.
>
> Note that we really want to have a Lookup object with the minimum set
> of "powers" to lookup public methods in public classes of the web
> application - otherwise the JPMS configuration becomes more
> complicated.
>
> We do have a simple reproducer that works in Java 11 and fails in 14.
> I am a JDK author so I can open an issue about this.

Yes, please file a JBS issue and I will look into it.   If the requested 
target class to be accessed through Lookup::in is exported, it should 
work because the set of classes that public lookups can access should 
not change.
thanks
Mandy



More information about the core-libs-dev mailing list