javalambdamethodhandlelambda-metafactory

LambdaMetafactory to access class on a different ClassLoader


I have this code which works fine:

Method getterMethod = Person.class.getDeclaredMethod("getName");

MethodHandles.Lookup lookup = MethodHandles.publicLookup();
Class<?> declaringClass = getterMethod.getDeclaringClass();
Class<?> returnType = getterMethod.getReturnType();
CallSite getterSite = LambdaMetafactory.metafactory(lookup,
    "apply",
    MethodType.methodType(Function.class),
    MethodType.methodType(Object.class, Object.class),
    lookup.findVirtual(declaringClass, getterMethod.getName(), MethodType.methodType(returnType)),
    MethodType.methodType(propertyType, declaringClass));

Function getterFunction = (Function) getterSite.getTarget().invokeExact();

But if the getterMethod is a method from a class loaded from a different ClassLoader, it throws:

Caused by: java.lang.invoke.LambdaConversionException: Invalid caller: java.lang.Object
    at java.lang.invoke.AbstractValidatingLambdaMetafactory.<init>(AbstractValidatingLambdaMetafactory.java:118)
    at java.lang.invoke.InnerClassLambdaMetafactory.<init>(InnerClassLambdaMetafactory.java:155)
    at java.lang.invoke.LambdaMetafactory.metafactory(LambdaMetafactory.java:299)

How do I pass my ClassLoader instance to the LambdaMetafactory?


Solution

  • As said by this answer, the lookup modes must include the private access to be accepted by the LambdaMetaFactory. Basically, this implies that either, the caller is the specified class, as it created the particular lookup instance, or the lookup class has enough trust to pass the lookup object to the code which did the actual call (which is e.g. implied when using invokedynamic pointing to a particular bootstrap method).

    Starting with Java 9, there is the method privateLookupIn(Class, MethodHandles.Lookup) to attempt to get a lookup object with private access to another class. As the documentation specifies, the access is checked against the module access rules, i.e. the caller must be “allowed to do deep reflection on the target class”. So the presence of the trust mentioned above, still is required, now in terms of module accessibility. I suppose, that’s the way to go for frameworks, where the code managed by the framework will be opened to the framework to support such access.

    If that’s not feasible, this answer contains an alternative, in case you are the creator of the class loader. It uses the class loader’s API to inject a new class which creates the lookup object and allows the creator to access it. There are variations imaginable, including making it secure by letting the synthetic class call back into the creator’s code to hand over the lookup object, instead of storing it in a field where everyone could read it.