I'm generating bytecode roughly equivalent to a class like:
final class MyCls {
final MethodHandle handle1;
final MethodHandle handle2;
// and so on
// This needs to invoke `handle1`, `handle2`, etc. in it somehow
final static myMethod() {
// ...
}
}
The class is fairly long-lived and I wish to call the MethodHandle
s from inside other methods, with ideally as little overhead as possible. What would be the best way to do this? The two ideas that come to mind are:
MethodHandle.invokeExact
calls on the fieldsinvokedynamic
somehow (although I think I'd still need the exactInvoker
?)The handles will vary in signatures (although their use-sites should all use the right signatures - I can detect/enforce that at codegen time).
Here's some extra context on what I'm actually doing. The classes represent compiled WASM modules, the method handles are imported functions, and each instance of the class in another instance of the WASM module.
Using MethodHandle
to represent imported functions isn't a necessity here - I could also accept something like a java.util.function.Function
or maybe even just a virtual method invocation. I do need a MethodHandle
representation sometimes, but I could summon one up from a virtual method too (and I could implement a virtual method manually calling a Function
too).
The module class instances themselves might end up being stored in static fields but that's not guaranteed. If there is a way to speed up that case, I could recommend users use that.
The simple answer is to just generate invokeExact
calls. With the code shape you've shown, there's no need to use invokedynamic
(in fact that doesn't seem possible, since invokedynamic
calls a bootstrap method which supplies the implementation dynamically).
Since the handles are stored instance fields, they are not seen as constants, and so the calls will be out of line, which adds overhead, as well as missed optimization opportunities due to a lack of inlining.
If you really want this to be as fast as possible, you'd need to generate a new class per combination of method handles you want to use, and store the method handles in static final
fields, or in the constant pool (for instance using constant pool patching, or hidden classes + class data + dynamic constants [1]).