2013-09-24 73 views
2

我有这个程序从java文件生成字节码。 而对于这个简单的测试()方法操作数堆栈不足

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 
+0

'javac'为相同的代码生成了什么? –

+0

您似乎已经优化了||真正的“。你已经把它当作'&&真' –

+0

不,那是不正确的。但即使它是,它仍然不能解释为什么我得到堆栈underrun错误。 (刚用'javac -''生成的字节码更新了这个问题,它几乎是一样的) –

回答

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,堆栈操作不足以通过有效性。

+0

(我是编译器新手(<2个月,请耐心等待))。但是又有什么问题?也许可以用更简单的方式来理解?你是说转换到java-6会解决问题吗? –

+0

@OneTwoThree - 大约5年前,Sun添加了一个新的“功能”字节码格式,要求您​​提供每个分支目标的堆栈内容表。他们显然加了这个来加速他们(非常可怜的)验证者,而不是简单地修复它。但它使得生成字节码PITA。 –

+0

@OneTwoThree我在说你需要堆栈信息。没有它,验证者会感到困惑。 –

0

生成的字节码问题是你的字节码不是从0开始的。我不知道前6个字节在做什么,但不管它是什么,它都不会被验证。

+0

我简化了这个文件,因为第一部分是用于字段声明和whatnot。 –

+0

@不幸的是,你的简化使其无法提供帮助。请发布完整的类文件。 – Antimony

0

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

+0

对不起,我不太明白你在这里的意思。这不是。顺便提一下,class文件。 –