Re: RFE: LambdaMetafactory to a class on another ClassLoader - an API gap?
You're right. I can confirm this works like a charm on JDK 9: MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(loadedClass, 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(); Thank you, Claes. With kind regards, Geoffrey De Smet On 12/02/18 11:37, Claes Redestad wrote: 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); import java.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 String JAR_FILE_PATH = System.getProperty("user.home") + "/.m2/repository/junit/junit/4.12/junit-4.12.jar"; private static final String CLASS_TO_LOAD = "junit.framework.TestResult"; private static final String METHOD_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 = "" style="color:rgb(0,0,128);font-weight:bold">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("");
Re: RFE: LambdaMetafactory to a class on another ClassLoader - an API gap?
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 ClassMETHOD_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
RFE: LambdaMetafactory to a class on another ClassLoader - an API gap?
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); import java.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 String JAR_FILE_PATH = System.getProperty("user.home") + "/.m2/repository/junit/junit/4.12/junit-4.12.jar"; private static final String CLASS_TO_LOAD = "junit.framework.TestResult"; private static final String METHOD_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