如何在 ByteBuddy 中安装和使用常量 MethodHandle?

How do I install and use a constant MethodHandle in ByteBuddy?

我正在玩 support that ByteBuddy has for constant MethodHandles

我正在尝试(有效地)在一个 class 上查找 MethodHandle,然后从 ByteBuddy 生成的子 class.

中使用它

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

我有一个 FieldDescription.Token 代表第一个 class 中的字段,还有一个 TypeDescription 代表第一个 class。从这些我可以得到这样的 FieldDescription.InDefinedShapenew FieldDescription.Latent(typeDescription, fieldDescriptionToken)。从 that 我可以得到这样的 JavaConstant.MethodHandleJavaConstant.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,它是 [的超级 class =19=]。如您所见,我正在尝试在此 MethodHandle 上调用 invokeExact(),其中 MethodHandle 希望存储为常量。

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

接下来,我将使用这个“字段 setter”MethodHandle 在 superclass 中设置的(愚蠢的)字段被命名为 fortyTwo 并且有一个Integer 的类型。您可能会猜到我想将其值设置为什么。这一切都很好,显然(还)与 ByteBuddy 没有任何关系,但它会。

编译一切正常。

当我 运行 我的代码时,在我有机会做任何事情之前,即在 class 加载期间,我得到一个 ClassFormatError 用于生成的子 class ByteBuddy 生成的。该错误抱怨此 subclass 已尝试定义具有无效签名的字段 (!):

java.lang.ClassFormatError: Field "fortyTwo" in class com/foo/bar/GeneratedSubclassOf$com$foo$bar$Baz753A95 has illegal signature "V"

我不是在定义这样的字段。 superclass 当然可以(见上文),而且它的类型,如前所述,是 java.lang.Integer。字段的访问级别无关紧要。

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

所以我的最后一个问题是:为什么 使用字段设置 MethodHandle 常量使它 看起来像 在这种情况下,ByteBuddy 试图在子class 中定义一个字段? 就好像在定义或存储常量时删除了字段的所属类型。

我认为这一切都与 ByteBuddy 支持常量 MethodHandles 的方式有关。难道是ByteBuddy在常量池中没有正确表示这种常量MethodHandle?或者这是 MethodHandle 本身的某种固有问题(也许只是字段设置)?

我确实注意到 there is a reference (indirectly) to void in the ByteBuddy code in question。这对我来说有一定的意义:如果你要合成一个设置字段的方法句柄,那么它的 return 类型将是 void。我想知道(天真地)这是否真的是传递给这里的正确类型,或者这是否可能接近问题所在。

另一方面,似乎为了解析一个方法句柄常量(在本例中)是一个“字段 setter”,或者 a method handle of kinds 1 through 4, inclusive, resolution must proceed according to the JVM's rules of field resolution. Those rules seem (to this naïve reader) to indicate that the descriptor used should be the field's descriptor. I think that, instead, ByteBuddy is using the descriptor of the synthesized method handle 在本例中是V(或void)。我天真的阅读让我认为至少在“field setters”的情况下,getDescriptor() 的 return 值应该是 [=107 中存在的唯一参数的类型=] getParameterTypes().

的值

如果我误诊了,我深表歉意;我还在学习中。

为了后代:这原来是 ByteBuddy 中的一个错误 for which I've submitted a PR