Reflecting method references

Tagir Valeev amaembo at gmail.com
Fri Jul 26 19:20:35 UTC 2024


Hello!

I'm continuing working on my toy decompiler-interpreter-asserts-library and
trying to restore a method reference from the code model. I've noticed that
there's a LambdaOp.methodReference() method. However, it works for only one
method reference case: instance method without qualifier expression. In
other cases, it returns null. More specifically:
String::isEmpty -- works
Integer::valueOf -- doesn't work
"xyz"::equals -- doesn't work
String::new -- doesn't work

Probably it's not implemented yet and still in the queue, I just wanted to
ensure that current behavior is not final.

Concentrating on a single working case (String::isEmpty), I need to
construct a functional lambda. We don't need to capture anything here,
which simplifies things a bit, but still the construction takes some
effort. My best attempt is the following (error handling removed for
brevity):

CoreOp.InvokeOp invokeOp = lambdaOp.methodReference().get();
MethodHandle handle = invokeOp.invokeDescriptor().resolveToHandle(lookup);
Class<?> fnClass = toClass(lambdaOp.functionalInterface());
// It looks like finding SAM is necessary. Is there an easier way of
getting it?
Method sam = Stream.of(fnClass.getMethods())
       .filter(m ->
Modifier.isAbstract(m.getModifiers())).findFirst().get();
MethodType samMethodType = MethodType.methodType(sam.getReturnType(),
sam.getParameterTypes());
MethodType dynamicType = toMethodType(lambdaOp.invokableType());
CallSite callSite = LambdaMetafactory.metafactory(lookup, sam.getName(),
MethodType.methodType(fnClass),
            samMethodType, handle, dynamicType);
Object lambda = callSite.dynamicInvoker().invoke();
... use lambda ...

The toMethodType() is the following helper:

private MethodType toMethodType(FunctionType functionType) {
  Class<?> rType = toClass(functionType.returnType());
  Class<?>[] pTypes =
functionType.parameterTypes().stream().map(this::toClass).toArray(Class[]::new);
  return MethodType.methodType(rType, pTypes);
}

Which in turn uses another helper toClass to convert TypeElement to Class<?>

So, quite a lot of code. The first thing which could be improved is
providing toMethodType(lookup) instance method directly inside
FunctionType. It could be nice to have a convenient bridge between
TypeElement world and MethodType world.

But probably it would be reasonable to resolve a LambdaOp directly to
CallSite, encapsulating all this stuff I write? In my application, I don't
need to do something fancy with nested lambdas, I just want to run them
normally. Not sure, but probably this could be reasonable in other
applications as well. Just something to think about.

With best regards,
Tagir Valeev
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/babylon-dev/attachments/20240726/7f6832bb/attachment.htm>


More information about the babylon-dev mailing list