1

I am compiling a simple language into JVM Bytecode and having some issues with Java object method calls. The verifier gives the error below

java.lang.VerifyError: (class: Test_1, method: main signature: ()V) Expecting to find object/array on stack

and below is the generated Java source code from my bytecodes by IntelliJ

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);
    }
}

which is exactly what I am trying to do. Creating an ArrayList, assigning a value and reading from it. The above code looks like a valid Java code to me.

Below is my bytecode

{
  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
}

I suspect something funny is going on along the lines 18-20, but I am not sure. The rest of the Bytecode instructions seem okay to me.

Why does the verifier complain about not finding an object on the stack?

fnisi
  • 1,181
  • 1
  • 14
  • 24

1 Answers1

5

The signature of ArrayList.get method at 22 is wrong.
The correct one is (I)Ljava/lang/Object;

apangin
  • 92,924
  • 10
  • 193
  • 247
  • 2
    Yup. The verifier sees that and realizes that after that `invokevirtual`, top-of-stack is a boolean. At which point you do a `checkcast` opcode, which causes the verifier to emit that exact error: checkcast cannot be executed when top-of-stack is a primitive. – rzwitserloot Aug 24 '21 at 12:52
  • Thanks - I fixed the issue! It is a shame that we do not have a Java bytecode debugger that one can execute the instructions one by one and catch errors more easily. – fnisi Aug 24 '21 at 12:57
  • 2
    @fnisi the possibility to debug bytecode instructions one by one has been discussed in [Debugging ASM-generated bytecode with JDB (or similar)](https://stackoverflow.com/q/49385759/2711488), but it won’t help you with verify errors, as most of them are thrown before any instruction has been executed. But JVMs like Hotspot (in up to date versions) usually give you more information than the single line you’ve posted. E.g., the offending instruction and its bytecode position is normally included. See, the beginning of [this question](https://stackoverflow.com/q/30059938/2711488) for an example. – Holger Aug 24 '21 at 16:14
  • 2
    @Holger `VerifyError` details are printed by the HotSpot built-in verifier. In OP's case, the error is thrown by `libverify` (aka "old" or "inferencing" verifier), presumably because of an old class version or missing `StackMapTable` attribute. – apangin Aug 24 '21 at 16:32
  • 2
    So raising the class file version number may enable more diagnostics. That’s a useful information, imho. – Holger Aug 24 '21 at 16:38
  • The class version I use is old - you are right. This is a limitation coming from the framework I use to generate bytecodes. Ta! – fnisi Aug 25 '21 at 00:02