ClassReader does not ensure that NEW has a label associated with it
This problem occurs in ASM 3.1.
Consider the following test case (inside some class):
public static class RegressionNew {
public static void foo(int x) {
new Integer(x == 0 ? 0 : 1);
}
}
@Test
public void asmRegressionNew() throws IOException {
ClassReader cr = new ClassReader(RegressionNew.class.getName());
TraceClassVisitor tcv = new TraceClassVisitor(new PrintWriter(System.out));
cr.accept(tcv, ClassReader.SKIP_DEBUG);
}
I think it doesn't matter which Java compiler is used, but I used Eclipse 3.4.
This results in:
public static foo(I)V
NEW java/lang/Integer
ILOAD 0
IFNE L0
ICONST_0
GOTO L1
L0
FRAME SAME1 L2
ICONST_1
L1
FRAME FULL [I] [L2 I]
INVOKESPECIAL java/lang/Integer.<init> (I)V
RETURN
MAXSTACK = 2
MAXLOCALS = 1
The frame before INVOKESPECIAL is incorrect, since it should contain a label
associated with NEW (it instead contains nonexistent label L2).
The following patch alleviates the problem:
ClassReader, near line 930 ("find the labels" phase):
case ClassWriter.TYPE_INSN:
if (Opcodes.NEW == opcode)
readLabel(w, labels);
case ClassWriter.SHORT_INSN:
case ClassWriter.LDCW_INSN:
case ClassWriter.FIELDORMETH_INSN:
case ClassWriter.IINC_INSN:
v += 3;
break;
Instead of
case ClassWriter.SHORT_INSN:
case ClassWriter.LDCW_INSN:
case ClassWriter.FIELDORMETH_INSN:
case ClassWriter.TYPE_INSN:
case ClassWriter.IINC_INSN:
v += 3;
break;
Now we have:
public static foo(I)V
L0
NEW java/lang/Integer
ILOAD 0
IFNE L1
ICONST_0
GOTO L2
L1
FRAME SAME1 L0
ICONST_1
L2
FRAME FULL [I] [L0 I]
INVOKESPECIAL java/lang/Integer.<init> (I)V
RETURN
MAXSTACK = 2
MAXLOCALS = 1
The frame before INVOKESPECIAL now contains the label L0, which was associated
with NEW. A more precise test case in Jasmin:
.class public RegressionNew
.super java/lang/Object
.method public static foo()V
.limit stack 1
.limit locals 0
new java/lang/Object
goto skip
skip:
invokespecial java/lang/Object/<init>()V
return
.end method