无法加载动态生成的字节码
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;
。
我已经成功地使用 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;
。