Code modification with Javassist generate a java.lang.VerifyError: Expecting to find integer on stack
Code modification with Javassist generate a java.lang.VerifyError: Expecting to find integer on stack
我使用 javassist 重写了一个名为 compile
的方法(它以字符串数组作为参数):我创建了一个新方法,其签名为 compile
方法(它是原始的 ) ,将实际的 compile
方法重命名为 compile$Impl
并添加了一些对我的 class 的调用。
复制是这样完成的:
CtMethod interceptMethod = CtNewMethod.copy(method, methodName, ctClass, null);
javassist 重写代码:
try {
com.company.CustomClass.instance().preintercept(this);
boolean result = compile$impl($$);
result = com.company.CustomClass.instance().dointercept(result, new Object[] { this , });
return result;
} finally {
com.company.CustomClass.instance().postintercept(this);
}
代码写在一个名为 body
的 StringBuffer 变量中,后面的变量的内容被写为新的 compile
方法的主体,如下所示:
interceptMethod.setBody(body.toString());
ctClass.addMethod(interceptMethod);
我的 com.company.CustomClass
有新的 compile
方法调用的方法:
public Object dointercept(boolean retval, Object.. args) {
return (Boolean)returned;
}
public static synchronized CustomClass instance() {
// _instance is already instanciated when this method is called
return _instance;
}
public void preintercept(Object arg) {
// some stuff before
}
public void postintercept(Object arg) {
// some stuff after
}
执行代码时出现验证错误:
java.lang.VerifyError: (class: org/eclipse/jdt/internal/compiler/batch/Main, method: compile signature: ([Ljava/lang/String;)Z) Expecting to find integer on stack
我已经将修改后的class的字节码写入磁盘,试图找出问题所在。这是我为新的 compile
方法得到的结果:
public boolean compile(java.lang.String[] arg0) {
try {
0 invokestatic 2130; /* com.company.CustomClass.instance() */
3 aload_0;
4 invokevirtual 2134; /* void preintercept(java.lang.Object arg0) */
7 aload_0;
8 aload_1;
9 invokevirtual 2136; /* boolean compile$impl(java.lang.String[] arg0) */
12 istore_2;
13 invokestatic 2130; /* com.company.CustomClass.instance() */
16 iload_2;
17 iconst_2;
18 anewarray 4; /* new java.lang.Object[] */
21 dup;
22 iconst_0;
23 aload_0;
24 aastore;
25 dup;
26 iconst_1;
27 aload_1;
28 aastore;
29 invokevirtual 2140; /* java.lang.Object dointercept(boolean arg0, java.lang.Object[] arg1) */
32 istore_2;
33 iload_2;
34 istore_3;
35 goto 17;
38 iload_3;
39 ireturn;
}
finally { /* covers bytes 0 to 40 */
40 astore 4;
42 invokestatic 2130; /* com.company.CustomClass.instance() */
45 aload_0;
46 invokevirtual 2143; /* void postintercept(java.lang.Object arg0) */
49 aload 4;
51 athrow;
52 invokestatic 2130; /* com.company.CustomClass.instance() */
55 aload_0;
56 invokevirtual 2143; /* void postintercept(java.lang.Object arg0) */
59 goto -21;
}
}
当我对 class 的字节码进行类型分析时,错误出现在第 32 行 istore_2
指令处。分析如下:
Type based analysis: compile([Ljava/lang/String;)Z
Offset Bytecode Stack before Stack after
----------------------------------------------------------------------------
0 invokestatic <empty> L
3 aload_0 L L, L
4 invokevirtual L, L <empty>
7 aload_0 <empty> L
8 aload_1 L L, L
9 invokevirtual L, L I
12 istore_2 I <empty>
13 invokestatic <empty> L
16 iload_2 L L, I
17 iconst_2 L, I L, I, I
18 anewarray L, I, I L, I, L
21 dup L, I, L L, I, L, L
22 iconst_0 L, I, L, L L, I, L, L, I
23 aload_0 L, I, L, L, I L, I, L, L, I, L
24 aastore L, I, L, L, I, L L, I, L
25 dup L, I, L L, I, L, L
26 iconst_1 L, I, L, L L, I, L, L, I
27 aload_1 L, I, L, L, I L, I, L, L, I, L
28 aastore L, I, L, L, I, L L, I, L
29 invokevirtual L, I, L L
32 istore_2 L <empty>
Error: Expecting to find I on stack, type on stack L.
33 iload_2 <empty> I
34 istore_3 I <empty>
35 goto <empty> <empty>
38 iload_3 <empty> I
39 ireturn I <empty>
40 astore L <empty>
42 invokestatic <empty> L
45 aload_0 L L, L
46 invokevirtual L, L <empty>
49 aload <empty> L
51 athrow L L
52 invokestatic <empty> L
55 aload_0 L L, L
56 invokevirtual L, L <empty>
59 goto <empty> <empty>
----------------------------------------------------------------------------
但是我不明白为什么存储一个int会有问题,我知道
我不使用任何整数(我确定遗漏了一些东西)。
谢谢
这是因为 doIntercept
正在返回一个 Boolean
Object
,并且它被存储为指向对象的指针(即出现在 29 处的 L
)。然后它尝试将该值存储到原始 boolean
中,即原始整数 I
.
如果使用显式转换,或在末尾添加.booleanValue()
com.company.CustomClass.instance().dointercept(result, new Object[] { this , }).booleanValue();
它可能有用吗?
问题是它没有将对象的 'automatic unboxing' 编译为基元。
https://docs.oracle.com/javase/tutorial/java/data/autoboxing.html
我使用 javassist 重写了一个名为 compile
的方法(它以字符串数组作为参数):我创建了一个新方法,其签名为 compile
方法(它是原始的 ) ,将实际的 compile
方法重命名为 compile$Impl
并添加了一些对我的 class 的调用。
复制是这样完成的:
CtMethod interceptMethod = CtNewMethod.copy(method, methodName, ctClass, null);
javassist 重写代码:
try {
com.company.CustomClass.instance().preintercept(this);
boolean result = compile$impl($$);
result = com.company.CustomClass.instance().dointercept(result, new Object[] { this , });
return result;
} finally {
com.company.CustomClass.instance().postintercept(this);
}
代码写在一个名为 body
的 StringBuffer 变量中,后面的变量的内容被写为新的 compile
方法的主体,如下所示:
interceptMethod.setBody(body.toString());
ctClass.addMethod(interceptMethod);
我的 com.company.CustomClass
有新的 compile
方法调用的方法:
public Object dointercept(boolean retval, Object.. args) {
return (Boolean)returned;
}
public static synchronized CustomClass instance() {
// _instance is already instanciated when this method is called
return _instance;
}
public void preintercept(Object arg) {
// some stuff before
}
public void postintercept(Object arg) {
// some stuff after
}
执行代码时出现验证错误:
java.lang.VerifyError: (class: org/eclipse/jdt/internal/compiler/batch/Main, method: compile signature: ([Ljava/lang/String;)Z) Expecting to find integer on stack
我已经将修改后的class的字节码写入磁盘,试图找出问题所在。这是我为新的 compile
方法得到的结果:
public boolean compile(java.lang.String[] arg0) {
try {
0 invokestatic 2130; /* com.company.CustomClass.instance() */
3 aload_0;
4 invokevirtual 2134; /* void preintercept(java.lang.Object arg0) */
7 aload_0;
8 aload_1;
9 invokevirtual 2136; /* boolean compile$impl(java.lang.String[] arg0) */
12 istore_2;
13 invokestatic 2130; /* com.company.CustomClass.instance() */
16 iload_2;
17 iconst_2;
18 anewarray 4; /* new java.lang.Object[] */
21 dup;
22 iconst_0;
23 aload_0;
24 aastore;
25 dup;
26 iconst_1;
27 aload_1;
28 aastore;
29 invokevirtual 2140; /* java.lang.Object dointercept(boolean arg0, java.lang.Object[] arg1) */
32 istore_2;
33 iload_2;
34 istore_3;
35 goto 17;
38 iload_3;
39 ireturn;
}
finally { /* covers bytes 0 to 40 */
40 astore 4;
42 invokestatic 2130; /* com.company.CustomClass.instance() */
45 aload_0;
46 invokevirtual 2143; /* void postintercept(java.lang.Object arg0) */
49 aload 4;
51 athrow;
52 invokestatic 2130; /* com.company.CustomClass.instance() */
55 aload_0;
56 invokevirtual 2143; /* void postintercept(java.lang.Object arg0) */
59 goto -21;
}
}
当我对 class 的字节码进行类型分析时,错误出现在第 32 行 istore_2
指令处。分析如下:
Type based analysis: compile([Ljava/lang/String;)Z
Offset Bytecode Stack before Stack after
----------------------------------------------------------------------------
0 invokestatic <empty> L
3 aload_0 L L, L
4 invokevirtual L, L <empty>
7 aload_0 <empty> L
8 aload_1 L L, L
9 invokevirtual L, L I
12 istore_2 I <empty>
13 invokestatic <empty> L
16 iload_2 L L, I
17 iconst_2 L, I L, I, I
18 anewarray L, I, I L, I, L
21 dup L, I, L L, I, L, L
22 iconst_0 L, I, L, L L, I, L, L, I
23 aload_0 L, I, L, L, I L, I, L, L, I, L
24 aastore L, I, L, L, I, L L, I, L
25 dup L, I, L L, I, L, L
26 iconst_1 L, I, L, L L, I, L, L, I
27 aload_1 L, I, L, L, I L, I, L, L, I, L
28 aastore L, I, L, L, I, L L, I, L
29 invokevirtual L, I, L L
32 istore_2 L <empty>
Error: Expecting to find I on stack, type on stack L.
33 iload_2 <empty> I
34 istore_3 I <empty>
35 goto <empty> <empty>
38 iload_3 <empty> I
39 ireturn I <empty>
40 astore L <empty>
42 invokestatic <empty> L
45 aload_0 L L, L
46 invokevirtual L, L <empty>
49 aload <empty> L
51 athrow L L
52 invokestatic <empty> L
55 aload_0 L L, L
56 invokevirtual L, L <empty>
59 goto <empty> <empty>
----------------------------------------------------------------------------
但是我不明白为什么存储一个int会有问题,我知道 我不使用任何整数(我确定遗漏了一些东西)。
谢谢
这是因为 doIntercept
正在返回一个 Boolean
Object
,并且它被存储为指向对象的指针(即出现在 29 处的 L
)。然后它尝试将该值存储到原始 boolean
中,即原始整数 I
.
如果使用显式转换,或在末尾添加.booleanValue()
com.company.CustomClass.instance().dointercept(result, new Object[] { this , }).booleanValue();
它可能有用吗?
问题是它没有将对象的 'automatic unboxing' 编译为基元。
https://docs.oracle.com/javase/tutorial/java/data/autoboxing.html