2013-09-24 72 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文件。 –