操作数堆栈欠载

时间:2013-09-24 15:03:42

标签: java bytecode

我有这个程序从java文件生成字节码。 对于这个简单的test()方法

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

,它将生成以下字节码

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

当我执行该类时,我不断得到Operand stack underrun in test(),我无法弄清楚为什么因为生成的字节码看起来不错(对我来说)

有人可以帮我调试吗?

(这是我追踪堆栈所做的事情

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

所以是的,堆叠看起来很好!)

编辑:这是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

3 个答案:

答案 0 :(得分:2)

当我尝试这个时

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

这样可行,但是如果我放弃堆栈帧调整,我会收到此错误

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  

简而言之,对于Java 7,堆栈操作不足以传递有效性。

答案 1 :(得分:0)

问题是你的字节码不是从偏移0开始的。我不知道前6个字节在做什么,但无论它是什么,它都不会验证。

答案 2 :(得分:0)

每个方法必须在.class中指定其堆栈帧大小max_stack,(此处为两个布尔值/整数)。在code attribute中,其中还包含说明。