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` 属性。
  • 因此,提高类文件版本号可能会启用更多诊断。这是一个有用的信息,恕我直言。

以上是Java 字节码错误:期望在堆栈上找到对象/数组的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>