问题描述
我在玩support that ByteBuddy has for constant MethodHandle
s。
我试图(有效地)在一个类上查找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,inclusive,resolution 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中的错误。