Trac #139: Correct stack map frames for Java 1.6.
diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/instr/MethodInstrumenterTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/instr/MethodInstrumenterTest.java
index eda8f6d..78987f2 100644
--- a/org.jacoco.core.test/src/org/jacoco/core/internal/instr/MethodInstrumenterTest.java
+++ b/org.jacoco.core.test/src/org/jacoco/core/internal/instr/MethodInstrumenterTest.java
@@ -118,97 +118,20 @@
}
@Test
- public void testVisitJumpInsnWithProbe_IFEQ() {
- testVisitJumpInsnWithProbe(Opcodes.IFEQ, Opcodes.IFNE);
- }
-
- @Test
- public void testVisitJumpInsnWithProbe_IFNE() {
- testVisitJumpInsnWithProbe(Opcodes.IFNE, Opcodes.IFEQ);
- }
-
- @Test
- public void testVisitJumpInsnWithProbe_IFLT() {
- testVisitJumpInsnWithProbe(Opcodes.IFLT, Opcodes.IFGE);
- }
-
- @Test
- public void testVisitJumpInsnWithProbe_IFGE() {
- testVisitJumpInsnWithProbe(Opcodes.IFGE, Opcodes.IFLT);
- }
-
- @Test
- public void testVisitJumpInsnWithProbe_IFGT() {
- testVisitJumpInsnWithProbe(Opcodes.IFGT, Opcodes.IFLE);
- }
-
- @Test
- public void testVisitJumpInsnWithProbe_IFLE() {
- testVisitJumpInsnWithProbe(Opcodes.IFLE, Opcodes.IFGT);
- }
-
- @Test
- public void testVisitJumpInsnWithProbe_IF_ICMPEQ() {
- testVisitJumpInsnWithProbe(Opcodes.IF_ICMPEQ, Opcodes.IF_ICMPNE);
- }
-
- @Test
- public void testVisitJumpInsnWithProbe_IF_ICMPNE() {
- testVisitJumpInsnWithProbe(Opcodes.IF_ICMPNE, Opcodes.IF_ICMPEQ);
- }
-
- @Test
- public void testVisitJumpInsnWithProbe_IF_ICMPLT() {
- testVisitJumpInsnWithProbe(Opcodes.IF_ICMPLT, Opcodes.IF_ICMPGE);
- }
-
- @Test
- public void testVisitJumpInsnWithProbe_IF_ICMPGE() {
- testVisitJumpInsnWithProbe(Opcodes.IF_ICMPGE, Opcodes.IF_ICMPLT);
- }
-
- @Test
- public void testVisitJumpInsnWithProbe_IF_ICMPGT() {
- testVisitJumpInsnWithProbe(Opcodes.IF_ICMPGT, Opcodes.IF_ICMPLE);
- }
-
- @Test
- public void testVisitJumpInsnWithProbe_IF_ICMPLE() {
- testVisitJumpInsnWithProbe(Opcodes.IF_ICMPLE, Opcodes.IF_ICMPGT);
- }
-
- @Test
- public void testVisitJumpInsnWithProbe_IF_ACMPEQ() {
- testVisitJumpInsnWithProbe(Opcodes.IF_ACMPEQ, Opcodes.IF_ACMPNE);
- }
-
- @Test
- public void testVisitJumpInsnWithProbe_IF_ACMPNE() {
- testVisitJumpInsnWithProbe(Opcodes.IF_ACMPNE, Opcodes.IF_ACMPEQ);
- }
-
- @Test
- public void testVisitJumpInsnWithProbe_IFNULL() {
- testVisitJumpInsnWithProbe(Opcodes.IFNULL, Opcodes.IFNONNULL);
- }
-
- @Test
- public void testVisitJumpInsnWithProbe_IFNONNULL() {
- testVisitJumpInsnWithProbe(Opcodes.IFNONNULL, Opcodes.IFNULL);
- }
-
- public void testVisitJumpInsnWithProbe(int opcode, int exOpcode) {
+ public void testVisitJumpInsnWithProbe() {
final Label label = new Label();
- instrumenter.visitJumpInsnWithProbe(opcode, label, 3);
+ instrumenter.visitJumpInsnWithProbe(Opcodes.IFEQ, label, 3);
+ instrumenter.visitMaxs(0, 0);
final Label l2 = new Label();
- expected.visitJumpInsn(exOpcode, l2);
+ expected.visitJumpInsn(Opcodes.IFEQ, l2);
+ expected.visitLabel(l2);
expected.visitVarInsn(Opcodes.ALOAD, 1);
expected.visitInsn(Opcodes.ICONST_3);
expected.visitInsn(Opcodes.ICONST_1);
expected.visitInsn(Opcodes.BASTORE);
expected.visitJumpInsn(Opcodes.GOTO, label);
- expected.visitLabel(l2);
+ expected.visitMaxs(3, 1);
assertEquals(expected, actual);
}
@@ -222,6 +145,7 @@
LabelInfo.setProbeId(L1, 1);
instrumenter.visitTableSwitchInsnWithProbes(3, 5, L0, new Label[] { L1,
L1, L2 });
+ instrumenter.visitMaxs(0, 0);
expected.visitTableSwitchInsn(3, 4, L0, new Label[] { L1, L1, L2 });
expected.visitLabel(L0);
@@ -236,6 +160,7 @@
expected.visitInsn(Opcodes.ICONST_1);
expected.visitInsn(Opcodes.BASTORE);
expected.visitJumpInsn(Opcodes.GOTO, new Label());
+ expected.visitMaxs(3, 1);
assertEquals(expected, actual);
}
@@ -249,6 +174,7 @@
LabelInfo.setProbeId(L1, 1);
instrumenter.visitLookupSwitchInsnWithProbes(L0,
new int[] { 10, 20, 30 }, new Label[] { L1, L1, L2 });
+ instrumenter.visitMaxs(0, 0);
expected.visitLookupSwitchInsn(L0, new int[] { 10, 20, 30 },
new Label[] { L1, L1, L2 });
@@ -264,6 +190,7 @@
expected.visitInsn(Opcodes.ICONST_1);
expected.visitInsn(Opcodes.BASTORE);
expected.visitJumpInsn(Opcodes.GOTO, new Label());
+ expected.visitMaxs(3, 1);
assertEquals(expected, actual);
}
diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/instr/ProbeVariableInserterTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/instr/ProbeVariableInserterTest.java
index f3fe05b..1de8fac 100644
--- a/org.jacoco.core.test/src/org/jacoco/core/internal/instr/ProbeVariableInserterTest.java
+++ b/org.jacoco.core.test/src/org/jacoco/core/internal/instr/ProbeVariableInserterTest.java
@@ -12,14 +12,17 @@
package org.jacoco.core.internal.instr;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
import java.util.Arrays;
+import org.jacoco.core.internal.flow.LabelInfo;
import org.junit.Test;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.commons.EmptyVisitor;
+import org.objectweb.asm.tree.FrameNode;
/**
* Unit tests for {@link ProbeVariableInserter}.
@@ -153,33 +156,20 @@
@Test
public void testVisitFrame() {
- class Recorder extends EmptyVisitor {
- int nLocal;
- Object[] local;
-
- @Override
- public void visitFrame(final int type, final int nLocal,
- final Object[] local, final int nStack, final Object[] stack) {
- this.nLocal = nLocal;
- this.local = local;
- }
- }
- final Recorder rec = new Recorder();
+ final FrameRecorder rec = new FrameRecorder();
ProbeVariableInserter i = new ProbeVariableInserter(0, "(J)V", rec);
// The first (implicit) frame must not be modified:
i.visitFrame(Opcodes.F_NEW, 2, new Object[] { "LFoo;", Opcodes.LONG },
- 0, null);
- assertEquals(2, rec.nLocal);
+ 0, new Object[0]);
assertEquals(Arrays.asList((Object) "LFoo;", Opcodes.LONG),
- Arrays.asList(rec.local));
+ rec.frame.local);
// Starting from the second frame on the probe variable is inserted:
i.visitFrame(Opcodes.F_NEW, 3, new Object[] { "LFoo;", Opcodes.LONG,
- "Ljava/lang/String;" }, 0, null);
- assertEquals(4, rec.nLocal);
+ "Ljava/lang/String;" }, 0, new Object[0]);
assertEquals(Arrays.asList((Object) "LFoo;", Opcodes.LONG, "[Z",
- "Ljava/lang/String;"), Arrays.asList(rec.local));
+ "Ljava/lang/String;"), rec.frame.local);
}
@Test(expected = IllegalStateException.class)
@@ -188,4 +178,91 @@
i.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
}
+ @Test
+ public void testInsertProbeFrame() {
+ final FrameRecorder rec = new FrameRecorder();
+ ProbeVariableInserter i = new ProbeVariableInserter(0, "(J)V", rec);
+
+ // The first (implicit) frame:
+ i.visitFrame(Opcodes.F_NEW, 2, new Object[] { "LFoo;", Opcodes.LONG },
+ 0, new Object[0]);
+
+ // There must be a label which is a multi-jump target:
+ Label label = new Label();
+ LabelInfo.setTarget(label);
+ LabelInfo.setTarget(label);
+ i.visitLabel(label);
+
+ // Insert a frame for this label:
+ i.visitFrame(Opcodes.F_NEW, 3, new Object[] { "LFoo;", Opcodes.LONG,
+ "Ljava/lang/String;" }, 0, new Object[0]);
+
+ // Insert this frame again:
+ rec.frame = null;
+ i.insertProbeFrame(label);
+
+ assertEquals(Arrays.asList((Object) "LFoo;", Opcodes.LONG, "[Z",
+ "Ljava/lang/String;"), rec.frame.local);
+ }
+
+ @Test
+ public void testInsertProbeFrameNoFrameForLabel() {
+ final FrameRecorder rec = new FrameRecorder();
+ ProbeVariableInserter i = new ProbeVariableInserter(0, "(J)V", rec);
+
+ // The first (implicit) frame:
+ i.visitFrame(Opcodes.F_NEW, 2, new Object[] { "LFoo;", Opcodes.LONG },
+ 0, new Object[0]);
+
+ // There must be a label which is a multi-jump target:
+ Label label = new Label();
+ LabelInfo.setTarget(label);
+ LabelInfo.setTarget(label);
+ i.visitLabel(label);
+
+ // Insert a frame for this label:
+ i.visitFrame(Opcodes.F_NEW, 3, new Object[] { "LFoo;", Opcodes.LONG,
+ "Ljava/lang/String;" }, 0, new Object[0]);
+
+ // Try to insert a frame for a different label:
+ rec.frame = null;
+ i.insertProbeFrame(new Label());
+ assertNull(rec.frame);
+ }
+
+ @Test
+ public void testInsertProbeFrameNoMultiTarget() {
+ final FrameRecorder rec = new FrameRecorder();
+ ProbeVariableInserter i = new ProbeVariableInserter(0, "(J)V", rec);
+
+ // The first (implicit) frame:
+ i.visitFrame(Opcodes.F_NEW, 2, new Object[] { "LFoo;", Opcodes.LONG },
+ 0, new Object[0]);
+
+ // The frame for this label isn't required:
+ Label label = new Label();
+ i.visitLabel(label);
+
+ // Insert a frame for this label:
+ i.visitFrame(Opcodes.F_NEW, 3, new Object[] { "LFoo;", Opcodes.LONG,
+ "Ljava/lang/String;" }, 0, new Object[0]);
+
+ // Inserting a frame for the label shouldn't work:
+ rec.frame = null;
+ i.insertProbeFrame(label);
+ assertNull(rec.frame);
+ }
+
+ private static class FrameRecorder extends EmptyVisitor {
+
+ FrameNode frame;
+
+ @Override
+ public void visitFrame(final int type, final int nLocal,
+ final Object[] local, final int nStack, final Object[] stack) {
+ assertEquals(Opcodes.F_NEW, type);
+ this.frame = new FrameNode(type, nLocal, local, nStack, stack);
+ }
+ }
+
}
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/instr/JumpProbe.java b/org.jacoco.core/src/org/jacoco/core/internal/instr/JumpProbe.java
new file mode 100644
index 0000000..295e5c3
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/internal/instr/JumpProbe.java
@@ -0,0 +1,70 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2011 Mountainminds GmbH & Co. KG and Contributors
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.core.internal.instr;
+
+import org.jacoco.core.internal.flow.LabelInfo;
+import org.objectweb.asm.Label;
+
+/**
+ * Probe to be inserted into a control flow edge along a jump instruction. This
+ * is implemented by replacing the original jump target by an intermediate
+ * target followed by the probe and a GOTO instruction back to the original
+ * target. This internal data structure stores all required information to
+ * append such jump probes at the end of a method.
+ */
+class JumpProbe {
+
+ private final Label target;
+
+ private final int probeid;
+
+ private final Label intermediate;
+
+ JumpProbe(final Label target, final int probeid) {
+ this.intermediate = new Label();
+ this.target = target;
+ this.probeid = probeid;
+ LabelInfo.setIntermediateLabel(target, this.intermediate);
+ }
+
+ JumpProbe(final Label target) {
+ this(target, LabelInfo.getProbeId(target));
+ }
+
+ /**
+ * Returns the original jump target.
+ *
+ * @return original jump target
+ */
+ public Label getTarget() {
+ return target;
+ }
+
+ /**
+ * Returns the corresponding probe id.
+ *
+ * @return corresponding probe id
+ */
+ public int getProbeId() {
+ return probeid;
+ }
+
+ /**
+ * Returns the label of the intermediate jump target
+ *
+ * @return intermediate jump target
+ */
+ public Label getIntermediate() {
+ return intermediate;
+ }
+
+}
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/instr/MethodInstrumenter.java b/org.jacoco.core/src/org/jacoco/core/internal/instr/MethodInstrumenter.java
index 1a3c25e..028578f 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/instr/MethodInstrumenter.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/instr/MethodInstrumenter.java
@@ -11,6 +11,9 @@
*******************************************************************************/
package org.jacoco.core.internal.instr;
+import java.util.ArrayList;
+import java.util.Collection;
+
import org.jacoco.core.internal.flow.IMethodProbesVisitor;
import org.jacoco.core.internal.flow.LabelInfo;
import org.objectweb.asm.Label;
@@ -28,6 +31,8 @@
private int accessorStackSize;
+ private Collection<JumpProbe> jumpProbes;
+
/**
* Create a new instrumenter instance for the given method.
*
@@ -44,6 +49,7 @@
final String desc, final IProbeArrayStrategy probeArrayStrategy) {
super(access, desc, mv);
this.probeArrayStrategy = probeArrayStrategy;
+ this.jumpProbes = null;
}
@Override
@@ -60,6 +66,8 @@
@Override
public void visitMaxs(final int maxStack, final int maxLocals) {
+ insertJumpProbes();
+
// Max stack size of the probe code is 3 which can add to the
// original stack size depending on the probe locations. The accessor
// stack size is an absolute maximum, as the accessor code is inserted
@@ -107,92 +115,28 @@
insertProbe(probeId);
mv.visitJumpInsn(Opcodes.GOTO, label);
} else {
- final Label l = new Label();
- mv.visitJumpInsn(getNegation(opcode), l);
- insertProbe(probeId);
- mv.visitJumpInsn(Opcodes.GOTO, label);
- mv.visitLabel(l);
+ final JumpProbe probe = new JumpProbe(label, probeId);
+ addJumpProbe(probe);
+ mv.visitJumpInsn(opcode, probe.getIntermediate());
}
}
- private int getNegation(final int opcode) {
- switch (opcode) {
- case Opcodes.IFEQ:
- return Opcodes.IFNE;
- case Opcodes.IFNE:
- return Opcodes.IFEQ;
- case Opcodes.IFLT:
- return Opcodes.IFGE;
- case Opcodes.IFGE:
- return Opcodes.IFLT;
- case Opcodes.IFGT:
- return Opcodes.IFLE;
- case Opcodes.IFLE:
- return Opcodes.IFGT;
- case Opcodes.IF_ICMPEQ:
- return Opcodes.IF_ICMPNE;
- case Opcodes.IF_ICMPNE:
- return Opcodes.IF_ICMPEQ;
- case Opcodes.IF_ICMPLT:
- return Opcodes.IF_ICMPGE;
- case Opcodes.IF_ICMPGE:
- return Opcodes.IF_ICMPLT;
- case Opcodes.IF_ICMPGT:
- return Opcodes.IF_ICMPLE;
- case Opcodes.IF_ICMPLE:
- return Opcodes.IF_ICMPGT;
- case Opcodes.IF_ACMPEQ:
- return Opcodes.IF_ACMPNE;
- case Opcodes.IF_ACMPNE:
- return Opcodes.IF_ACMPEQ;
- case Opcodes.IFNULL:
- return Opcodes.IFNONNULL;
- case Opcodes.IFNONNULL:
- return Opcodes.IFNULL;
- }
- throw new AssertionError(opcode);
- }
-
public void visitTableSwitchInsnWithProbes(final int min, final int max,
final Label dflt, final Label[] labels) {
- // 1. Calculate intermediate labels:
LabelInfo.resetDone(dflt);
LabelInfo.resetDone(labels);
final Label newDflt = createIntermediate(dflt);
final Label[] newLabels = createIntermediates(labels);
mv.visitTableSwitchInsn(min, max, newDflt, newLabels);
-
- // 2. Insert probes:
- insertIntermediateProbes(dflt, labels);
}
public void visitLookupSwitchInsnWithProbes(final Label dflt,
final int[] keys, final Label[] labels) {
- // 1. Calculate intermediate labels:
LabelInfo.resetDone(dflt);
LabelInfo.resetDone(labels);
final Label newDflt = createIntermediate(dflt);
final Label[] newLabels = createIntermediates(labels);
mv.visitLookupSwitchInsn(newDflt, keys, newLabels);
-
- // 2. Insert probes:
- insertIntermediateProbes(dflt, labels);
- }
-
- private Label createIntermediate(final Label label) {
- final Label intermediate;
- if (LabelInfo.getProbeId(label) == LabelInfo.NO_PROBE) {
- intermediate = label;
- } else {
- if (LabelInfo.isDone(label)) {
- intermediate = LabelInfo.getIntermediateLabel(label);
- } else {
- intermediate = new Label();
- LabelInfo.setIntermediateLabel(label, intermediate);
- LabelInfo.setDone(label);
- }
- }
- return intermediate;
}
private Label[] createIntermediates(final Label[] labels) {
@@ -203,23 +147,43 @@
return intermediates;
}
- private void insertIntermediateProbe(final Label label) {
- final int probeId = LabelInfo.getProbeId(label);
- if (probeId != LabelInfo.NO_PROBE && !LabelInfo.isDone(label)) {
- mv.visitLabel(LabelInfo.getIntermediateLabel(label));
- insertProbe(probeId);
- mv.visitJumpInsn(Opcodes.GOTO, label);
- LabelInfo.setDone(label);
+ private Label createIntermediate(final Label label) {
+ final Label intermediate;
+ if (LabelInfo.getProbeId(label) == LabelInfo.NO_PROBE) {
+ intermediate = label;
+ } else {
+ if (LabelInfo.isDone(label)) {
+ intermediate = LabelInfo.getIntermediateLabel(label);
+ } else {
+ final JumpProbe probe = new JumpProbe(label);
+ addJumpProbe(probe);
+ intermediate = probe.getIntermediate();
+ LabelInfo.setDone(label);
+ }
+ }
+ return intermediate;
+ }
+
+ private void addJumpProbe(final JumpProbe probe) {
+ if (jumpProbes == null) {
+ jumpProbes = new ArrayList<JumpProbe>();
+ }
+ jumpProbes.add(probe);
+ }
+
+ private void insertJumpProbes() {
+ if (jumpProbes != null) {
+ for (final JumpProbe probe : jumpProbes) {
+ insertJumpProbe(probe);
+ }
}
}
- private void insertIntermediateProbes(final Label dflt, final Label[] labels) {
- LabelInfo.resetDone(dflt);
- LabelInfo.resetDone(labels);
- insertIntermediateProbe(dflt);
- for (final Label l : labels) {
- insertIntermediateProbe(l);
- }
+ private void insertJumpProbe(final JumpProbe probe) {
+ mv.visitLabel(probe.getIntermediate());
+ insertProbeFrame(probe.getTarget());
+ visitProbe(probe.getProbeId());
+ mv.visitJumpInsn(Opcodes.GOTO, probe.getTarget());
}
}
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/instr/ProbeVariableInserter.java b/org.jacoco.core/src/org/jacoco/core/internal/instr/ProbeVariableInserter.java
index 680ed7e..db576c3 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/instr/ProbeVariableInserter.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/instr/ProbeVariableInserter.java
@@ -11,17 +11,26 @@
*******************************************************************************/
package org.jacoco.core.internal.instr;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.jacoco.core.internal.flow.LabelInfo;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodAdapter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
+import org.objectweb.asm.tree.FrameNode;
/**
- * Simplified version of ASM's
- * {@link org.objectweb.asm.commons.LocalVariablesSorter} that inserts a local
- * variable at the very beginning of the method. This avoids maintaining mapping
- * tables and prevents ASM bug #314563.
+ * Internal utility to add a local variable for the probe array at the beginning
+ * of every method. The adapter adjusts all other local variables and frames
+ * accordingly. In addition the adapter records the frames at certain labels to
+ * re-insert them if required for jump probes.
+ *
+ * Basically this is a simplified version of ASM's
+ * {@link org.objectweb.asm.commons.LocalVariablesSorter} to avoid expensive
+ * mapping tables and prevents ASM bug #314563.
*/
class ProbeVariableInserter extends MethodAdapter {
@@ -33,6 +42,10 @@
private boolean firstFrame = true;
+ private Label lastLabel;
+
+ private Map<Label, FrameNode> probeFrames;
+
/**
* Creates a new {@link ProbeVariableInserter}.
*
@@ -54,6 +67,13 @@
}
variableIdx = idx;
variable = pos;
+ lastLabel = null;
+ }
+
+ @Override
+ public void visitLabel(final Label label) {
+ mv.visitLabel(label);
+ lastLabel = label;
}
@Override
@@ -95,15 +115,6 @@
"ClassReader.accept() should be called with EXPAND_FRAMES flag");
}
- if (firstFrame) {
- // The first frame is generated by ASM and represents the implicit
- // frame derived from the method signature only. This frame must not
- // yet be modified.
- mv.visitFrame(type, nLocal, local, nStack, stack);
- firstFrame = false;
- return;
- }
-
final Object[] newLocal = new Object[nLocal + 1];
for (int i = 0; i <= local.length; i++) {
if (i < variableIdx) {
@@ -116,7 +127,43 @@
}
newLocal[i] = InstrSupport.DATAFIELD_DESC;
}
- mv.visitFrame(type, nLocal + 1, newLocal, nStack, stack);
+
+ if (lastLabel != null) {
+ if (LabelInfo.isMultiTarget(lastLabel)) {
+ // Create map instance only if required:
+ if (probeFrames == null) {
+ probeFrames = new HashMap<Label, FrameNode>();
+ }
+ probeFrames.put(lastLabel, new FrameNode(type, nLocal + 1,
+ newLocal, nStack, stack));
+ }
+ lastLabel = null;
+ }
+
+ if (firstFrame) {
+ // The first frame is generated by ASM and represents the implicit
+ // frame derived from the method signature only. This frame must not
+ // yet be modified.
+ mv.visitFrame(type, nLocal, local, nStack, stack);
+ firstFrame = false;
+ } else {
+ mv.visitFrame(type, nLocal + 1, newLocal, nStack, stack);
+ }
+ }
+
+ /**
+ * Inserts the frame again that was inserted after the given label.
+ *
+ * @param label
+ * label of the frame to insert
+ */
+ protected void insertProbeFrame(final Label label) {
+ if (probeFrames != null) {
+ final FrameNode frame = probeFrames.get(label);
+ if (frame != null) {
+ frame.accept(mv);
+ }
+ }
}
}
diff --git a/org.jacoco.doc/docroot/doc/changes.html b/org.jacoco.doc/docroot/doc/changes.html
index 339c0fe..3a0d0d3 100644
--- a/org.jacoco.doc/docroot/doc/changes.html
+++ b/org.jacoco.doc/docroot/doc/changes.html
@@ -27,6 +27,11 @@
<li>Added support for TestNG to Ant task Coverage (Track #144).</li>
</ul>
+<h3>Fixed Bugs</h3>
+<ul>
+ <li>Calculate correct stack map frames for Java 1.6 branches (SF #139).</li>
+</ul>
+
<h3>Non-functional Changes</h3>
<ul>
<li>API documentation cleanup (Track #140).</li>