One Two Three
One Two Three

Reputation: 23517

Operand stack underrun

I have this program to generate bytecode from a java file. And for this simple test() method

public void test()
{
    boolean a = false;
    if (a || true)
    {
         boolean b = false;
    }
}

, it would generate the follow piece of bytecode

public void test()
  Code:
   7:   iconst_0
   8:   istore_1
   9:   iload_1
   10:  ifne    13
   13:  iconst_0
   14:  istore_2
   15:  return

When I execute the class, I keep getting Operand stack underrun in test(), which I couldn't figure out why because the generated bytecode looks good (to me)

Could anyone help me debug this?

(Here's what I did to trace the stack

public void test()
  Code:
   7:   iconst_0
 (1 on the stack)
   8:   istore_1
 (0 on the stack)
   9:   iload_1
 (1 on the stack)
   10:  ifne    13
 (0 on the stack)
   13:  iconst_0
 (1 on the stack)
   14:  istore_2
 (0 on the stack)
   15:  return

So yeah, the stack looks fine to me!)

EDIT: Here's the generated bytecode by javac

public void test();
  Code:
   0:   iconst_0
   1:   istore_1
   2:   iload_1
   3:   ifne    6
   6:   iconst_0
   7:   istore_2
   8:   return

Upvotes: 2

Views: 1161

Answers (3)

Peter Lawrey
Peter Lawrey

Reputation: 533720

When I try this

public class MainDump implements Opcodes {

    public static byte[] dump() throws Exception {

        ClassWriter cw = new ClassWriter(0);
        FieldVisitor fv;
        MethodVisitor mv;
        AnnotationVisitor av0;

        cw.visit(V1_7, ACC_PUBLIC + ACC_SUPER, "Main", null, "java/lang/Object", null);

        cw.visitSource("Main.java", null);

        {
            mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
            mv.visitCode();
            Label l0 = new Label();
            mv.visitLabel(l0);
            mv.visitLineNumber(1, l0);
            mv.visitVarInsn(ALOAD, 0);
            mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
            mv.visitInsn(RETURN);
            Label l1 = new Label();
            mv.visitLabel(l1);
            mv.visitLocalVariable("this", "LMain;", null, l0, l1, 0);
            mv.visitMaxs(1, 1);
            mv.visitEnd();
        }
        {
            mv = cw.visitMethod(ACC_PUBLIC, "test", "()V", null, null);
            mv.visitCode();
            mv.visitInsn(ICONST_0);
            mv.visitVarInsn(ISTORE, 1);
            mv.visitVarInsn(ILOAD, 1);
            Label l2 = new Label();
            mv.visitJumpInsn(IFEQ, l2);
            mv.visitInsn(ICONST_0);
            mv.visitVarInsn(ISTORE, 2);
            mv.visitLabel(l2);
            mv.visitFrame(Opcodes.F_APPEND, 1, new Object[]{Opcodes.INTEGER}, 0, null);
            mv.visitInsn(RETURN);
            mv.visitMaxs(1, 3);
            mv.visitEnd();
        }
        cw.visitEnd();

        return cw.toByteArray();
    }

    public static void main(String... ignored) throws Exception {
        Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
        defineClass.setAccessible(true);

        byte[] dump = dump();
        defineClass.invoke(Thread.currentThread().getContextClassLoader(), dump, 0, dump.length);
        new Main().test();
    }
}

This works, however if I drop the stack frame adjustment, I get this error

Exception in thread "main" java.lang.VerifyError: Expecting a stackmap frame at branch target 8
Exception Details:
  Location:
    Main.test()V @3: ifeq
  Reason:
    Expected stackmap frame at this location.
  Bytecode:
    0000000: 033c 1b99 0005 033d b1  

In short, for Java 7, the stack operations are not enough to pass validity.

Upvotes: 2

Joop Eggen
Joop Eggen

Reputation: 109593

Every method must specify its stack frame size, max_stack, (two booleans/ints here) in the .class. In the code attribute, which also contains the instructions.

Upvotes: 0

Antimony
Antimony

Reputation: 39451

The problem is that your bytecode doesn't start at offset 0. I don't know what the first 6 bytes are doing, but whatever it is, it's not going to verify.

Upvotes: 0

Related Questions