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

Reply via email to