I've found the InvokeDynamic
class and have made it work with a static
method handle acquired via MethodHandles.Lookup.findStatic()
.
Now I am trying to do the same thing, but with a virtual method handle acquired via MethodHandles.Lookup.findVirtual()
.
I can cause my bootstrap method to run, and I make sure in my bootstrap method that I'm returning a ConstantCallSite(mh)
, where mh
is the result of calling MethodHandles.Lookup.findVirtual()
. (This part all works fine, i.e. I understand how "indy" works.)
However, when I use the resulting Implementation
as the argument to an intercept()
call, I cannot pass the actual object on which the method represented by the method handle is to be invoked. This is due to the withArgument()
method being used for two contradictory purposes.
Here is my recipe:
Implementation impl =
InvokeDynamic.bootstrap(myBootstrapDescription, someOtherConstantArgumentsHere)
.invoke(theMethodName, theMethodReturnType)
// 0 is the object on which I want to invoke my virtual-method-represented-by-a-method-handle;
// 1 is the sole argument that the method actually takes.
.withArgument(0, 1);
There are some problems here.
Specifically, it seems that withArgument()
is used by ByteBuddy for two things, not just one:
- Specifying the parameter types that will be used to build a
MethodType
that will be supplied to the bootstrap method. Let's say my virtual method takes one argument.
- Specifying how the instrumented method's arguments are passed to the actual method handle execution.
If I have supplied only one argument, the receiver type is left unbound and execution of the resulting MethodHandle
cannot happen, because I haven't passed an argument that will be used for the receiver type "slot". If I accordingly supply two arguments to (1) above (as I do in my recipe), then the method handle is not found by my bootstrap method, because the supplied MethodType
indicates that the method I am searching for requires two arguments, and my actual method that I'm finding only takes one.
Finally, I can work around this (and validate my hypothesis) by doing some fairly ugly stuff in my bootstrap method:
- First, I deliberately continue to pass two arguments, not one, even though my method only takes two arguments:
withArgument(0, 1)
- In my bootstrap method, I now know that the
MethodType
it will receive will be "incorrect" (it will have two parameter types, not one, where the first parameter type will represent the receiver type). I drop the first parameter using MethodType#dropParameterTypes(int, int)
.
- I call
findVirtual()
with the new MethodType
. It returns a MethodType
with two parameter types: the receiver type that it adds automatically, and the existing non-dropped parameter type.
(More simply I can just pass a MethodType
as a constant to my bootstrap method via, for example, JavaConstant.MethodType.of(myMethodDescription)
or built however I like, and ignore the one that ByteBuddy synthesizes. It would still be nice if there were instead a way to control the MethodType
that ByteBuddy supplies (is obligated to supply) to the bootstrap method.)
When I do things like this in my bootstrap method, my recipe works. I'd prefer not to tailor my bootstrap method to ByteBudddy, but will here if I have to.
Is it a bug that ByteBuddy does not seem to allow InvokeDynamic
to specify the ingredients for a MethodType
directly, without also specifying the receiver?
question from:
https://stackoverflow.com/questions/65893808/how-can-i-invoke-a-virtual-method-handle-using-bytebuddys-invokedynamic-class