Context: I've been benchmarking the difference between using invokedynamic
and manually generating bytecode (this is in the context of deciding whether a compiler targeting the JVM should emit more verbose "traditional" bytecode or just an invokedynamic
call with a clever bootstrap method). In doing this, it has been pretty straightforward to map bytecode into MethodHandles
combinators that are at least as fast, with the exception of tableswitch
.
Question: Is there a trick to mimic tableswitch
using MethodHandle
? I tried mimicking it with a jump table: using a constant MethodHandle[]
, indexing into that with arrayElementGetter
, then calling the found handle with MethodHandles.invoker
. However, that ended up being around 50% slower than the original bytecode when I ran it through JMH.
Here's the code for producing the method handle:
private static MethodHandle makeProductElement(Class<?> receiverClass, List<MethodHandle> getters) {
MethodHandle[] boxedGetters = getters
.stream()
.map(getter -> getter.asType(getter.type().changeReturnType(java.lang.Object.class)))
.toArray(MethodHandle[]::new);
MethodHandle getGetter = MethodHandles // (I)H
.arrayElementGetter(MethodHandle[].class)
.bindTo(boxedGetters);
MethodHandle invokeGetter = MethodHandles.permuteArguments( // (RH)O
MethodHandles.invoker(MethodType.methodType(java.lang.Object.class, receiverClass)),
MethodType.methodType(java.lang.Object.class, receiverClass, MethodHandle.class),
1,
0
);
return MethodHandles.filterArguments(invokeGetter, 1, getGetter);
}
Here's the initial bytecode (which I'm trying to replace with one invokedynamic
call)
public java.lang.Object productElement(int);
descriptor: (I)Ljava/lang/Object;
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=3, args_size=2
0: iload_1
1: istore_2
2: iload_2
3: tableswitch { // 0 to 2
0: 28
1: 38
2: 45
default: 55
}
28: aload_0
29: invokevirtual #62 // Method i:()I
32: invokestatic #81 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
35: goto 67
38: aload_0
39: invokevirtual #65 // Method s:()Ljava/lang/String;
42: goto 67
45: aload_0
46: invokevirtual #68 // Method l:()J
49: invokestatic #85 // Method java/lang/Long.valueOf:(J)Ljava/lang/Long;
52: goto 67
55: new #87 // class java/lang/IndexOutOfBoundsException
58: dup
59: iload_1
60: invokestatic #93 // Method java/lang/Integer.toString:(I)Ljava/lang/String;
63: invokespecial #96 // Method java/lang/IndexOutOfBoundsException."<init>":(Ljava/lang/String;)V
66: athrow
67: areturn
StringConcatFactory
, to name a practical example, is capable of both, generating code ore composing handles and, of course, the compiler generatedinvokedynamic
instruction is not affected by that runtime choice. – HexStringConcatFactory
? I can't find any example of constructing aMethodHandle
directly from bytecode. – Fletcherprivate
methods of the lookup class. This has been made a standard functionality in JDK 15, previous versions usedsun.misc.Unsafe
for that. – HexdefineHiddenClass
isn't used at all for the string concat stuff, but I did find one legit (and unsurprising) use injava.lang.invoke.InnerClassLambdaMetafactory
. If you want to put together an example of doing this for sometableswitch
instruction (even if it doesn't match exactly my question's function) I'll accept that – Fletcher