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>