I am trying to work out how to use LambdaMetafactory to generate a usable callSite.
Here's my latest Groovy script attempt. I have tried multiple permutations of parameters and cannot get the second getter based example to work.
The first example I did eventually get to work generating a Supplier from a closure after much fiddling and reading around the Java documentation and Stack Overflow.
import java.lang.invoke.LambdaMetafactory
import java.lang.invoke.MethodHandle
import java.lang.invoke.MethodHandles
import java.lang.invoke.MethodType
import java.util.function.Supplier
/**
* LambdaMetafactory example with closure - works
*/
Closure myClosure = {"hello from closure"}
MethodHandles.Lookup lookup= MethodHandles.lookup()
def delegateImpl = lookup.findVirtual(Closure.class, "call", MethodType.methodType (Object.class))
//now get a callSite for the handle - https://wttech.blog/blog/2020/method-handles-and-lambda-metafactory/
java.lang.invoke.CallSite closureCallSite = LambdaMetafactory.metafactory(
lookup,
"get",
MethodType.methodType(Supplier.class, Closure.class),
MethodType.methodType (Object.class),
delegateImpl,
MethodType.methodType(String)
)
MethodHandle closureFactory = closureCallSite.getTarget()
Supplier closureLambda = closureFactory.bindTo(myClosure).invokeWithArguments()
def res = closureLambda.get()
println res
/**
* LambdaMetafactory example with standard class - cant get to work with any combinations
*/
class ExampleClass {
private String value = "hello from getter"
ExampleClass() {} //constructor
String getValue () {return value}
void setValue (String val) {value = val}
}
ExampleClass instance = new ExampleClass()
MethodHandle getterDelegateImpl = lookup.findVirtual(ExampleClass.class, "getValue", MethodType.methodType (String.class))
java.lang.invoke.CallSite getterCallSite = LambdaMetafactory.metafactory(
lookup,
"get",
MethodType.methodType(Supplier.class, ExampleClass),
MethodType.methodType (Object.class),
getterDelegateImpl,
MethodType.methodType(String.class)
)
MethodHandle classFactory = getterCallSite.getTarget()
Supplier lambda = classFactory.bindTo(instance).invokeWithArguments()
def ret = lambda.get ()
println ret
When you run this, the first example works and I can get a valid callSite to get a dynamic Supplier reference from Closure. The second example takes the same approach but using a standard class with a getter method.
hello from closure
Caught: java.lang.invoke.LambdaConversionException: Exception finding constructor
java.lang.invoke.LambdaConversionException: Exception finding constructor
at script.testLambdaMetafactory.run(testLambdaMetafactory.groovy:69)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
Caused by: java.lang.IllegalAccessException: no such constructor: org.codehaus.groovy.vmplugin.v8.IndyInterface$$InjectedInvoker_0x0000000800dc2000$$Lambda$261/0x0000000800dc6a78.<init>(ExampleClass)void/invokeSpecial
... 4 more
Caused by: java.lang.LinkageError: bad method type alias: (ExampleClass)void not visible from class org.codehaus.groovy.vmplugin.v8.IndyInterface$$InjectedInvoker_0x0000000800dc2000$$Lambda$261/0x0000000800dc6a78
... 4 more
I have tried many permutations of types, numbers of params for methodTypes to no effect.
What do I have to do to generate workable code given that I want to generate invokeDynamic references that work?
Ok - seems that part of this was the fact i using a groovy script and decalred my bean class in that script. LamdbaMetafactory didn't like. So i separated the bean class into its own class file
now you can write some tests - i've shown three here, one using access via a Supplier and the other by a Function generated interface. If your accessing non static methods, you need to bind the instance, and it needs to be declared in the invokedType param.
If your invoking a static method - you dont need to declare the bean in the invokedType, and you dont need to bind an instance
These three tests are now working
I hope this may safe several hours thrashing on how to use the LambdaMetafactory. It is however remains very pernickety to use, and understand how to drive it, should you want to use it
To help improve the fiddley use i've tried to generate a couple of static 'helper' methods that disguises some of the fiddly type matching for you.
and some tests to show this working along side the raw access i was using to cross check my MethodTypes used for the direct use of the LambdaFactory. This has not tested all the options rigorously, but the sample cases tested should work.