如何在ByteBuddy中安装和使用恒定的MethodHandle?

问题描述

我在玩support that ByteBuddy has for constant MethodHandles

我试图(有效地)在一个类上查找MethodHandle,然后从ByteBuddy生成的子类中使用它。

(我知道我可以使用静态MethodHandle字段和类型初始值设定项来执行此操作,但我想使用此常量支持进行尝试。)

我有一个FieldDescription.Token代表第一类的字段,还有一个TypeDescription代表第一类的字段。从这些我可以得到一个FieldDescription.InDefinedShape,像这样:new FieldDescription.Latent(typeDescription,fieldDescriptionToken)。从中,我可以得到一个JavaConstant.MethodHandle,像这样:JavaConstant.MethodHandle.ofSetter(fieldDescriptionLatent)效果很好。

然后我这样做:

// Call invokeExact() on the MethodHandle I looked up,but
// call it on that MethodHandle as a constant pool entry.
builder
  .intercept(MethodCall.invoke(INVOKE_EXACT)
             .on(new JavaConstantValue(javaConstantMethodHandle),MethodHandle.class)
             // etc.

通过这样做,我正在使用on overload that takes a StackManipulation在这种情况下是JavaConstantValue,它包装了JavaConstant,它是JavaConstant.MethodHandle的超类。如您所见,我正在尝试在此invokeExact()调用MethodHandle,希望将MethodHandle存储为常量。

我的第一个问题是:这是正确的食谱吗?

接下来,我将使用此“字段设置器” MethodHandle在超类中设置的(愚蠢)字段名为fortyTwo,类型为Integer。您可能会猜到我要将其值设置为什么。 ?一切都很好,并且(显然)与ByteBuddy无关,但是可以。

一切都可以编译。

运行代码时,在没有机会做任何事情之前(即,在类加载期间),我为ByteBuddy生成生成子类得到了ClassFormatError。该错误表明此 subclass 试图定义一个具有无效签名的字段(!):

java.lang.classFormatError: Field "fortyTwo" in class com/foo/bar/GeneratedSubclassOf$com$foo$bar$Baz$26753A95 has illegal signature "V"

我没有定义这样的字段。当然,超类确实存在(请参见上文),并且如前所述,其类型为java.lang.Integer。字段的访问级别无关紧要。

我查看了TypeDescription所包含的DynamicType.Unloaded(显然是在加载之前),并且没有fieldTokens,即ByteBuddy确实没有尝试实际定义字段在子类中。看来,我正在使用的食谱中的某些内容使它看起来……呃,验证者?我猜?真的不知道吗?常量代表的MethodHandle试图操纵子类上的字段,当然不存在这样的字段(我猜想是“ V”是void的字节码签名,可能是认的)。

所以我的最后一个问题是:为什么这样做使用字段设置MethodHandle使其看起来像 ByteBuddy试图定义在这种情况下是子类中的字段?好像是在定义或存储常量或其他内容删除了字段的所有权类型。

我认为所有这些都与ByteBuddy如何支持常量MethodHandle有关。可能是ByteBuddy不能在常量池中正确表示这种常量MethodHandle吗?还是MethodHandle本身存在某种固有的问题(也许只是现场设置)?

我确实注意到there is a reference (indirectly) to void in the ByteBuddy code in question。这对我来说有一定的道理:如果您要合成设置字段的方法句柄,则其返回类型将为void。我不知道(天真)这实际上是否是传递给这里的正确类型,或者是否可能在问题所在的地方。

另一方面,似乎是为了解析方法句柄常量(在这种情况下)是“字段设置器”或a method handle of kinds 1 through 4,inclusiveresolution must proceed according to the JVM's rules of field resolution。这些规则(对于这个天真的读者来说)似乎indicate that the descriptor used should be the field's descriptor。我认为,相反,是ByteBuddy is using the descriptor of the synthesized method handle在这种情况下为V(或void)。我的幼稚阅读使我认为,至少在“字段设置器”的情况下,getDescriptor()的返回值应该是getParameterTypes()的返回值中存在的唯一参数的类型。

如果我误诊了我的道歉;我还在学习。

解决方法

为了后代:这实际上是ByteBuddy for which I've submitted a PR中的错误。