无法加载动态生成的字节码

Fail to load a dynamically generated bytecodes

我已经成功地使用 ASM 将动态生成的字节码转储到一个文件中,但是无法加载它。错误信息是:

java.lang.ClassFormatError: JVMCFRE102 field signature invalid; class=TGWD, offset=0
    at java.lang.ClassLoader.defineClass(ClassLoader.java:364)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:284)
    at java.lang.invoke.ByteCodeClassLoader.run(ByteCodeClassLoader.java:20)

BytecodeClassLoader是我写到这里的Class。 javap -v 结果也如下所示。

Classfile /C:/temp/TGWD.class
  Last modified Mar 10, 2015; size 750 bytes
  MD5 checksum 462ec39a439ab0d30c676eb92a93fd5a
public class TGWD extends java.lang.invoke.BaseTemplate
  minor version: 0
  major version: 51
  flags: ACC_PUBLIC, ACC_SUPER

Constant pool:
   #1 = Utf8               TGWD
   #2 = Class              #1             //  TGWD
   #3 = Utf8               java/lang/invoke/BaseTemplate
   #4 = Class              #3             //  java/lang/invoke/BaseTemplate
   #5 = Utf8               guard
   #6 = Utf8               Ljava/lang/invoke/MethodHandle;
   #7 = Utf8               trueTarget
   #8 = Utf8               falseTarget
   #9 = Utf8               <init>
  #10 = Utf8               (Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;)V
  #11 = Utf8               java.lang.invoke.MethodHandle
  #12 = NameAndType        #5:#11         //  guard:java.lang.invoke.MethodHandle
  #13 = Fieldref           #2.#12         //  TGWD.guard:java.lang.invoke.MethodHandle
  #14 = NameAndType        #7:#11         //  trueTarget:java.lang.invoke.MethodHandle
  #15 = Fieldref           #2.#14         //  TGWD.trueTarget:java.lang.invoke.MethodHandle
  #16 = NameAndType        #8:#11         //  falseTarget:java.lang.invoke.MethodHandle
  #17 = Fieldref           #2.#16         //  TGWD.falseTarget:java.lang.invoke.MethodHandle
  #18 = Utf8               eval
  #19 = Utf8               ()V
  #20 = Utf8               Hello
  #21 = Utf8               (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
  #22 = Utf8               java/lang/Throwable
  #23 = Class              #22            //  java/lang/Throwable
  #24 = NameAndType        #5:#6          //  guard:Ljava/lang/invoke/MethodHandle;
  #25 = Fieldref           #2.#24         //  TGWD.guard:Ljava/lang/invoke/MethodHandle;
  #26 = Utf8               java/lang/invoke/MethodHandle
  #27 = Class              #26            //  java/lang/invoke/MethodHandle
  #28 = Utf8               invokeExact
  #29 = Utf8               (Ljava/lang/String;)Z
  #30 = NameAndType        #28:#29        //  invokeExact:(Ljava/lang/String;)Z
  #31 = Methodref          #27.#30        //  java/lang/invoke/MethodHandle.invokeExact:(Ljava/lang/String;)Z
  #32 = NameAndType        #7:#6          //  trueTarget:Ljava/lang/invoke/MethodHandle;
  #33 = Fieldref           #2.#32         //  TGWD.trueTarget:Ljava/lang/invoke/MethodHandle;
  #34 = NameAndType        #28:#21        //  invokeExact:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
  #35 = Methodref          #27.#34        //  java/lang/invoke/MethodHandle.invokeExact:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
  #36 = NameAndType        #8:#6          //  falseTarget:Ljava/lang/invoke/MethodHandle;
  #37 = Fieldref           #2.#36         //  TGWD.falseTarget:Ljava/lang/invoke/MethodHandle;
  #38 = Utf8               Code
  #39 = Utf8               StackMapTable
  #40 = Utf8               Exceptions
{
  final java.lang.invoke.MethodHandle guard;
    flags: ACC_FINAL


  final java.lang.invoke.MethodHandle trueTarget;
    flags: ACC_FINAL


  final java.lang.invoke.MethodHandle falseTarget;
    flags: ACC_FINAL


  public TGWD(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle);
    flags: ACC_PUBLIC, ACC_VARARGS

    Code:
      stack=0, locals=4, args_size=4
         0: aload_0       
         1: aload_1       
         2: bipush        0
         4: aaload        
         5: putfield      #13                 // Field guard:java.lang.invoke.MethodHandle
         8: aload_0       
         9: aload_1       
        10: bipush        1
        12: aaload        
        13: putfield      #15                 // Field trueTarget:java.lang.invoke.MethodHandle
        16: aload_0       
        17: aload_1       
        18: bipush        2
        20: aaload        
        21: putfield      #17                 // Field falseTarget:java.lang.invoke.MethodHandle
        24: return        

  public void eval();
    flags: ACC_PUBLIC

    Code:
      stack=0, locals=1, args_size=1
         0: return        

  public java.lang.String Hello(java.lang.String, java.lang.String) throws java.lang.Throwable;
    flags: ACC_PUBLIC

    Code:
      stack=0, locals=3, args_size=3
         0: aload_0       
         1: getfield      #25                 // Field guard:Ljava/lang/invoke/MethodHandle;
         4: aload_1       
         5: invokevirtual #31                 // Method java/lang/invoke/MethodHandle.invokeExact:(Ljava/lang/String;)Z
         8: ifeq          21
        11: aload_0       
        12: getfield      #33                 // Field trueTarget:Ljava/lang/invoke/MethodHandle;
        15: aload_1       
        16: aload_2       
        17: invokevirtual #35                 // Method java/lang/invoke/MethodHandle.invokeExact:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
        20: areturn       
        21: aload_0       
        22: getfield      #37                 // Field falseTarget:Ljava/lang/invoke/MethodHandle;
        25: aload_1       
        26: aload_2       
        27: invokevirtual #35                 // Method java/lang/invoke/MethodHandle.invokeExact:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
        30: areturn       
      StackMapTable: number_of_entries = 1
           frame_type = 21 /* same */

    Exceptions:
      throws java.lang.Throwable
}

我能知道生成的字节码哪里错了吗?谢谢。 我还在此处附加了 class 文件Class File..

以下信息可能有用: 1、 根据堆栈,我猜问题出在三个现场成员身上。我使用以下代码生成这些字段:

FieldVisitor fv;
fv = cw.visitField(ACC_FINAL, 'guard', "Ljava/lang/..", null, null);
fv.visitEnd();

我把ACC_FINAL改成了ACC_PRIVATE。结果是相同的,除了三个字段成员的 none 显示在 javap -v 结果中。

2、The Base class BaseTemplate 是一个空抽象 class 定义为:

public abstract class BaseTemplate { }

============================== 更新:

更改我原始代码中的 putfield 指令后

mv.visitFieldInsn(PUTFIELD, className, list.get(i).name(), list.get(i).type());

mv.visitFieldInsn(PUTFIELD, className, list.get(i).name(),Utils.getFieldDesc(list.get(i).type()));,现在class加载成功。原因是错误的字段签名集(如@Holger 的回答)。

您使用 "java.lang.invoke.MethodHandle" 而不是正确的 "Ljava/lang/invoke/MethodHandle;" 作为字段签名生成了构造函数。这可以在以下行的 javap 输出中看到:

     5: putfield      #13             // Field guard:java.lang.invoke.MethodHandle
…
    13: putfield      #15             // Field trueTarget:java.lang.invoke.MethodHandle
…
    21: putfield      #17             // Field falseTarget:java.lang.invoke.MethodHandle

Hello 方法正确使用:

     1: getfield      #25             // Field guard:Ljava/lang/invoke/MethodHandle;
…
    12: getfield      #33             // Field trueTarget:Ljava/lang/invoke/MethodHandle;
…
    22: getfield      #37             // Field falseTarget:Ljava/lang/invoke/MethodHandle;

这也解释了相当大的常量池。项目 #11 包含错误的 java.lang.invoke.MethodHandle 签名,随后由构造函数使用的后续项目 #12 - #17 使用该签名。这些与 Hello 方法中使用的项目 #25、#33 和 #37 不同。在正确生成的 class 中,这些指令在常量池中共享相同的项目(它们都间接引用存储在该 class 文件的项目 #6 中的正确签名 Ljava/lang/invoke/MethodHandle;