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@openjdk.java.net
http://mail.openjdk.java.net/mailman/listinfo/mlvm-dev

_______________________________________________
mlvm-dev mailing list
mlvm-dev@openjdk.java.net
http://mail.openjdk.java.net/mailman/listinfo/mlvm-dev

Reply via email to