简体   繁体   English

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

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

I'm playing with the support that ByteBuddy has for constant MethodHandle s .我正在玩ByteBuddy 对常量MethodHandle s 的支持

I am trying to (effectively) look up a MethodHandle on one class, and then use it from a ByteBuddy-generated subclass.我试图(有效地)在一个MethodHandle上查找 MethodHandle,然后从 ByteBuddy 生成的子类中使用它。

(I am aware that I could do this using a static MethodHandle field and a type initializer, but I wanted to try it using this constant support.) (我知道我可以使用 static MethodHandle字段和类型初始值设定项来执行此操作,但我想尝试使用此常量支持。)

I have a FieldDescription.Token representing a field in the first class, and a TypeDescription representing the first class. From these I can get a FieldDescription.InDefinedShape like this: new FieldDescription.Latent(typeDescription, fieldDescriptionToken) .我有一个FieldDescription.Token代表第一个 class 中的一个字段,还有一个TypeDescription代表第一个 class。从这些我可以得到一个FieldDescription.InDefinedShape像这样: new FieldDescription.Latent(typeDescription, fieldDescriptionToken) From that I can get a JavaConstant.MethodHandle like this: JavaConstant.MethodHandle.ofSetter(fieldDescriptionLatent) .那里我可以获得这样的JavaConstant.MethodHandleJavaConstant.MethodHandle.ofSetter(fieldDescriptionLatent) This works fine.这很好用。

Then I do this:然后我这样做:

// 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.

By doing this, I am using the on overload that takes a StackManipulation , which in this case is the JavaConstantValue which wraps a JavaConstant , which is a superclass of JavaConstant.MethodHandle .通过这样做,我使用了带有 StackManipulation 的on重载StackManipulation在这种情况下,它是包装JavaConstant JavaConstantValue它是JavaConstant.MethodHandle的超类。 As you can see, I am attempting to call invokeExact() on this MethodHandle where the MethodHandle is hopefully stored as a constant.如您所见,我正在尝试在此MethodHandle上调用invokeExact() ,希望将MethodHandle存储为常量。

My first question is: is this the right recipe?我的第一个问题是:这是正确的食谱吗?

Next, the (stupid) field I am going to set in the superclass using this "field setter" MethodHandle is named fortyTwo and has a type of Integer .接下来,我将使用这个“字段设置器” MethodHandle在超类中设置的(愚蠢的)字段被命名为fortyTwo并且类型为Integer You can probably guess what I want to set its value to.您可能会猜到我想将其值设置为什么。 This is all fine, and obviously doesn't (yet) have anything to do with ByteBuddy, but it will.这一切都很好,显然(还)与 ByteBuddy 没有任何关系,但它会。

Everything compiles fine.一切都很好。

When I run my code, before I ever get a chance to do anything, ie during class load, I get a ClassFormatError for the generated subclass that ByteBuddy generates.当我运行我的代码时,在我有机会做任何事情之前,即在 class 加载期间,我得到了 ByteBuddy 生成的生成子类的ClassFormatError The error complains that this subclass has attempted to define a field (:) with an invalid signature:该错误抱怨此子类试图定义一个具有无效签名的字段 (:):

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

I am not defining such a field.我没有定义这样的字段。 The superclass does, of course (see above), and its type, as previously noted, is java.lang.Integer .超类当然可以(见上文),并且如前所述,它的类型是java.lang.Integer The access level of the field does not matter.该字段的访问级别无关紧要。

I have looked at the TypeDescription contained by the DynamicType.Unloaded (before it is loaded, obviously) and there are no fieldTokens , ie ByteBuddy is truly not attempting to actually define a field in the subclass.我查看了DynamicType.Unloaded包含的TypeDescription (显然是在加载之前)并且没有fieldTokens ,即 ByteBuddy 确实没有尝试在子类中实际定义一个字段。 It appears that something in the recipe I'm using makes it look to the…uh, verifier?看来我正在使用的食谱中的某些东西让它看起来……呃,验证者? I guess?我猜? don't really know?真的不知道吗? that the MethodHandle represented by the constant is trying to manipulate the field on the subclass , and of course no such field exists (I'm going to guess that " V " is the bytecode signature for void and is probably the default).由常量表示的MethodHandle试图操纵子类上的字段,当然不存在这样的字段(我猜测“ V ”是void的字节码签名,可能是默认值)。

So my last question is: Why does the use of a field-setting MethodHandle constant make it look like ByteBuddy is attempting to define a field in the subclass in this case?所以我的最后一个问题是:在这种情况下,为什么使用字段设置MethodHandle常量会让人觉得ByteBuddy 试图在子类中定义一个字段? It's as if the owning type of the field is dropped when the constant is defined or stored or something.就好像在定义或存储常量时字段的所属类型被删除了一样。

I presume this all has to do with how ByteBuddy supports constant MethodHandle s.我认为这一切都与 ByteBuddy 支持常量MethodHandle的方式有关。 Could it be that ByteBuddy is not properly representing this kind of constant MethodHandle in the constant pool?难道是MethodHandle Or is this some sort of inherent problem with (maybe just field-setting) MethodHandle s themselves?或者这是MethodHandle本身的某种固有问题(可能只是字段设置)?

I do notice that there is a reference (indirectly) to void in the ByteBuddy code in question .我确实注意到在有问题的 ByteBuddy 代码中有一个(间接地)对void的引用 This makes a certain amount of sense to me: if you're going to synthesize a method handle that sets a field, then its return type will be void .这对我来说有一定的意义:如果您要合成一个设置字段的方法句柄,那么它的返回类型将为void I wonder though (naïvely) if this is in fact the proper type to pass here, or if this may be near where the problem lies.我想知道(天真地)这是否真的是传递给这里的正确类型,或者这是否可能接近问题所在。

On the other hand, it appears that in order to resolve a method handle constant that (in this case) is a "field setter", or a method handle of kinds 1 through 4, inclusive , resolution must proceed according to the JVM's rules of field resolution .另一方面,似乎为了解析方法句柄常量(在这种情况下)是“字段设置器”,或1 到 4 种方法句柄,包括在内解析必须根据 JVM 的规则进行场分辨率 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 which in this case would be V (or void ).认为,ByteBuddy 正在使用合成方法句柄的描述符,在本例中为V (或void )。 My naïve reading makes me think that at least in the case of "field setters" the return value of getDescriptor() should instead be the type of the sole parameter present in the return value of getParameterTypes() .我天真的阅读让我认为至少在“字段设置器”的情况下, getDescriptor()的返回值应该是getParameterTypes()的返回值中存在的唯一参数的类型。

My apologies if I have misdiagnosed this;如果我误诊了这一点,我深表歉意; I am still learning.我还在学习。

For posterity: this turns out to be a bug in ByteBuddy for which I've submitted a PR .对于后代:事实证明这是 ByteBuddy中的一个错误,我已经为此提交了 PR

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM