RFE: LambdaMetafactory to a class on another ClassLoader - an API gap?

Claes Redestad claes.redestad at oracle.com
Mon Feb 12 10:37:55 UTC 2018


Hi Geoffrey,

since 9 there's MethodHandles.privateLookup(Class<?>, Lookup)[1] to get 
hold of a Lookup with the full, private capabilities of a specific 
class, as required by LambdaMetafactory.

Replacing line 62 in your sample with the following should work:

         MethodHandles.Lookup lookup = 
MethodHandles.privateLookupIn(loadedClass, MethodHandles.lookup());

HTH

/Claes

[1] 
https://docs.oracle.com/javase/9/docs/api/java/lang/invoke/MethodHandles.html#privateLookupIn-java.lang.Class-java.lang.invoke.MethodHandles.Lookup-

On 2018-02-12 10:54, Geoffrey De Smet wrote:
> Hi all,
>
> As a developer for a Java framework, I 'd like to use the MethodHandle API
> or the LambdaMetafactory as an alternative to code generation or 
> reflection
> to call getter/setters on my user's classes, often loaded by another 
> classloader.
>
> Because non-static MethodHandles are currently too slow (see the other 
> thread and [1]),
> I am forced to use LambdaMetafactory.
> However, if the user class is loaded by another (non-parent) classloader,
> I can't use LambdaMetafactory on it.
>
> The method MethodHandles.lookup(ClassLoader) doesn't exist.
> Might this be an API gap?
>
> Proof of code below, which
>     throws java.lang.ClassNotFoundException: junit.framework.TestResult
> when calling
>     Object returnValue = getterFunction.apply(instance);
>
> importjava.io.File;
> import java.lang.invoke.CallSite;
> import java.lang.invoke.LambdaMetafactory;
> import java.lang.invoke.MethodHandle;
> import java.lang.invoke.MethodHandles;
> import java.lang.invoke.MethodType;
> import java.net.URL;
> import java.net.URLClassLoader;
> import java.util.function.Function;
>
> public class LambdaMetafactoryApiGap {
>
>      // Proof 1 private static final StringJAR_FILE_PATH = System.getProperty("user.home") +"/.m2/repository/junit/junit/4.12/junit-4.12.jar";
>      private static final StringCLASS_TO_LOAD ="junit.framework.TestResult";
>      private static final StringMETHOD_NAME ="errorCount";
>      private static final Class<?>METHOD_RETURN_TYPE = Integer.TYPE;
>
>      // Proof 2 // private static final String JAR_FILE_PATH = 
> "/home/ge0ffrey/.m2/repository/org/optaplanner/optaplanner-benchmark/7.5.0.Final/optaplanner-benchmark-7.5.0.Final.jar"; 
> // private static final String CLASS_TO_LOAD = 
> "org.optaplanner.benchmark.impl.result.PlannerBenchmarkResult"; // 
> private static final String METHOD_NAME = "getName"; // private static 
> final Class<?> METHOD_RETURN_TYPE = String.class; public static void main(String[] args)throws Throwable {
>          try {
>              LambdaMetafactoryApiGap.class.getClassLoader().loadClass(CLASS_TO_LOAD);
>              System.out.println("The class (" +CLASS_TO_LOAD +") is already on the normal classpath. Cheater.");
>          }catch (ClassNotFoundException e) {
>              // Ok, class is not yet on this normal classpath }
>
>          URL url =new File(JAR_FILE_PATH).toURI().toURL();
>          URLClassLoader classLoader =new URLClassLoader(new URL[]{url});
>          Class<?> loadedClass = classLoader.loadClass(CLASS_TO_LOAD);
>
>          invokeWithMethodHandle(loadedClass);
>          invokeWithLambdaMetafactory(loadedClass);
>      }
>
>      private static void invokeWithMethodHandle(Class<?> loadedClass)throws Throwable {
>          System.out.println("MethodHandle");
>          System.out.println("============");
>          MethodHandles.Lookup lookup = MethodHandles.lookup();
>          MethodHandle methodHandle = lookup
>                  // Call method signature: public int errorCount() {...} .findVirtual(loadedClass,METHOD_NAME, MethodType.methodType(METHOD_RETURN_TYPE))
>                  // Cast input and output to Object (we can't import the input class in 
> this java source) .asType(MethodType.methodType(Object.class, Object.class));
>
>          Object instance = loadedClass.newInstance();
>          Object returnValue = methodHandle.invokeExact(instance);
>          System.out.println("MethodHandle invoke returns: " + returnValue);
>          System.out.println(" but non-static MethodHandles are very slow, so as a framework 
> developer I prefer LambdaMetafactory.");
>          System.out.println();
>      }
>
>      private static void invokeWithLambdaMetafactory(Class<?> loadedClass)throws Throwable {
>          System.out.println("LambdaMetafactory");
>          System.out.println("=================");
>          // Notice that MethodHandles.lookup(ClassLoader) doesn't exist MethodHandles.Lookup lookup = MethodHandles.lookup();
>          CallSite site = LambdaMetafactory.metafactory(lookup,
>                      "apply",
>                      MethodType.methodType(Function.class),
>                      MethodType.methodType(Object.class, Object.class),
>                      lookup.findVirtual(loadedClass,METHOD_NAME, MethodType.methodType(METHOD_RETURN_TYPE)),
>                      MethodType.methodType(METHOD_RETURN_TYPE, loadedClass));
>          Function getterFunction = (Function) site.getTarget().invokeExact();
>
>          Object instance = loadedClass.newInstance();
>          // Throws java.lang.ClassNotFoundException: junit.framework.TestResult Object returnValue = getterFunction.apply(instance);
>          System.out.println("LambdaMetafactory invoke returns: " + returnValue);
>      }
>
> }
>
> [1] 
> https://www.optaplanner.org/blog/2018/01/09/JavaReflectionButMuchFaster.html
>
> Wkr,
> Geoffrey De Smet
>
>
>
> _______________________________________________
> mlvm-dev mailing list
> mlvm-dev at openjdk.java.net
> http://mail.openjdk.java.net/mailman/listinfo/mlvm-dev

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.openjdk.java.net/pipermail/mlvm-dev/attachments/20180212/2f055c0f/attachment-0001.html>


More information about the mlvm-dev mailing list