Reputation: 23517
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
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
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
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