我正在使用ByteBuddy 对常量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
带 a 的重载,StackManipulation
在这种情况下,JavaConstantValue
它包装了 a JavaConstant
,它是 的超类JavaConstant.MethodHandle
。如您所见,我试图在希望将其存储为常量的地方调用invokeExact()
它。MethodHandle
MethodHandle
我的第一个问题是:这是正确的食谱吗?
接下来,我将使用这个“字段设置器”在超类中设置的(愚蠢的)字段MethodHandle
被命名fortyTwo
并且类型为Integer
. 您可能会猜到我想将其值设置为什么。这一切都很好,显然(还)与 ByteBuddy 没有任何关系,但它会的。
一切都编译得很好。
当我运行我的代码时,在我有机会做任何事情之前,即在类加载期间,我得到了ClassFormatError
ByteBuddy 生成的生成子类。该错误抱怨此子类已尝试使用无效签名定义字段(!):
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
。难道是 ByteBuddyMethodHandle
在常量池中没有正确表示这种常量?或者这是(也许只是字段设置)本身的某种固有问题MethodHandle
?
我确实注意到在有问题的 ByteBuddy 代码中有一个引用(间接地)void
。这对我来说有一定的意义:如果你要合成一个设置字段的方法句柄,那么它的返回类型将是void
. 不过,我想知道(天真地)这是否实际上是在此处传递的正确类型,或者这是否可能靠近问题所在。
另一方面,似乎为了解析(在这种情况下)是“字段设置器”的方法句柄常量,或者类型 1 到 4(包括)的方法句柄,解析必须根据 JVM 的规则进行场分辨率。这些规则似乎(对于这个天真的读者)表明使用的描述符应该是字段的描述符。我认为,相反,ByteBuddy正在使用合成方法句柄的描述符,在这种情况下将是V
(或void
)。我的幼稚阅读使我认为至少在“字段设置器”的情况下,返回值getDescriptor()
应该是返回值中存在的唯一参数的类型getParameterTypes()
.
如果我误诊了,我深表歉意;我仍在学习。