1

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 MethodHandles 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:

  • Generating explicit MethodHandle.invokeExact calls on the fields
  • Using invokedynamic 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).

Update

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.

Alec
  • 31,829
  • 7
  • 67
  • 114
  • The fields are not `static`? – Johannes Kuhn May 01 '22 at 14:40
  • No, they aren’t – Alec May 01 '22 at 14:44
  • That makes things more complicated - as they can't be easily inlined in that case. There are some tricks if you store `MyCls` instances in a static final field - and the final fields are trusted, such as when `MyCls` is either a record or a hidden class. – Johannes Kuhn May 01 '22 at 14:50
  • The handles would be user inputs to a “module” (whose instances are the class instances), so not static in general, and I can’t control/see whether the class instances are in static final fields either. – Alec May 01 '22 at 14:54
  • Can you add more information to your question, in particular, how you intend to use the class and stuff? – Johannes Kuhn May 01 '22 at 15:04
  • @JohannesKuhn I added in some extra context - let me know if that helps. I am interested in more general feedback - I just didn't want to make the question broad and advice based since that's not usually advised on SO. – Alec May 01 '22 at 16:04
  • 1
    If you're the one generating this class, why can't you put the method handles in `static final` fields (or the constant pool)? Presumably they won't need to change for a particular module, and you generate a class per module right? – Jorn Vernee May 01 '22 at 18:09
  • @JornVernee They actually _may_ change for a single module. The compiled module might even be re-instantiated multiple times with different imports. – Alec May 01 '22 at 18:12
  • @Alec I would suggest that you load a separate class for each WASM module instantiation. (At least that is what I did when I transpiled WASM to java bytecode.) – Johannes Kuhn May 01 '22 at 19:55

1 Answers1

3

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]).

Jorn Vernee
  • 31,735
  • 4
  • 76
  • 93
  • I definitely _could_ use `invokedynamic` with a `this` argument and a getter bootstrap argument, but I buy that I won't get anything more out of it than `invokeExact`. – Alec May 01 '22 at 18:41
  • @Alec the bootstrap method of `invokedynamic` can only be passed constants as arguments, so it doesn't seem possible to pass `this`. – Jorn Vernee May 01 '22 at 18:43
  • Right, I mean the bootstrap method could take a getter as a bootstrap argument (not `this`, obviously) and then generate a method handle that accepts `this` + the handle arguments, invokes the getter using `this`, then invokes the handle gotten as an output. – Alec May 01 '22 at 18:45
  • @Alec Ok, I see what you mean. – Jorn Vernee May 01 '22 at 18:47