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

时间:2015-03-11 03:04:48

标签: java classloader java-bytecode-asm bytecode-manipulation

我已成功使用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是我在这里写的课程。 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 File ..

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

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

我将ACC_FINAL更改为ACC_PRIVATE。结果是相同的,除了javap -v结果中没有显示三个字段成员。

2,Base类BaseTemplate是一个空的抽象类,定义为:

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()));,类加载现在成功。原因是wronlg字段签名集(如@Holger中的答案)。

1 个答案:

答案 0 :(得分:1)

您使用"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不同。在正确生成的类中,这些指令共享常量池中的相同项(它们都间接引用存储在此类文件中的项#6中的正确签名Ljava/lang/invoke/MethodHandle;