Java 字节码错误:期望在堆栈上找到对象/数组
我正在将一种简单的语言编译成 JVM 字节码,并且在 Java 对象方法调用方面存在一些问题。验证器给出以下错误
java.lang.VerifyError: (class: Test_1, method: main signature: ()V) Expecting to find object/array on stack
以下是 IntelliJ 从我的字节码生成的 Java 源代码
import java.util.ArrayList;
public final class Test_1 {
public static void main() {
ArrayList var1 = new ArrayList();
var1.add(19);
int var2 = (Integer)var1.get(0);
}
}
这正是我想要做的。创建一个 ArrayList,分配一个值并从中读取。上面的代码对我来说看起来像是一个有效的 Java 代码。
下面是我的字节码
{
public static void main();
descriptor: ()V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=2, args_size=0
0: new #9 // class java/util/ArrayList
3: dup
4: invokespecial #12 // Method java/util/ArrayList."<init>":()V
7: astore_1
8: aload_1
9: bipush 19
11: invokestatic #16 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
14: invokevirtual #26 // Method java/util/ArrayList.add:(Ljava/lang/Object;)Z
17: pop
18: aload_1
19: astore_0
20: aload_0
21: iconst_0
22: invokevirtual #34 // Method java/util/ArrayList.get:(I)Z
25: checkcast #2 // class java/lang/Integer
28: invokevirtual #11 // Method java/lang/Integer.intValue:()I
31: istore_1
32: return
}
我怀疑沿着 18-20 行发生了一些有趣的事情,但我不确定。其余的字节码指令对我来说似乎没问题。
为什么验证器会抱怨在堆栈上找不到对象?
回答
ArrayList.get方法在 22 处的签名是错误的。
正确的是(I)Ljava/lang/Object;
- 是的。验证者看到并意识到在`invokevirtual`之后,栈顶是一个布尔值。此时,您执行“checkcast”操作码,这会导致验证器发出确切的错误:当栈顶是原语时,无法执行 checkcast。
- @fnisi 在 [Debugging ASM-generated bytecode with JDB (or similar)](/sf/ask/3457003161/) 中已经讨论了逐一调试字节码指令的可能性,但这无济于事你有验证错误,因为它们中的大多数是在执行任何指令之前抛出的。但是像 Hotspot 这样的 JVM(在最新版本中)通常会为您提供比您发布的单行更多的信息。例如,通常包括违规指令及其字节码位置。以 [this question](/sf/ask/2104195691/) 的开头为例。
- @Holger `VerifyError` 详细信息由 HotSpot 内置验证器打印。在 OP 的情况下,错误是由 `libverify`(又名“旧”或“推理”验证器)抛出的,大概是因为旧的类版本或缺少 `StackMapTable` 属性。
- 因此,提高类文件版本号可能会启用更多诊断。这是一个有用的信息,恕我直言。