GitHub #286: Fix missing probes

Fix missing probes at the beginning of try/catch blocks when the block
starts as a jump target. This should restore exec file compatibility
with 0.7.2. Basically a revert of commit e4a474c
diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/flow/LabelFlowAnalyzerTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/flow/LabelFlowAnalyzerTest.java
index 6d27874..440b4f3 100644
--- a/org.jacoco.core.test/src/org/jacoco/core/internal/flow/LabelFlowAnalyzerTest.java
+++ b/org.jacoco.core.test/src/org/jacoco/core/internal/flow/LabelFlowAnalyzerTest.java
@@ -107,6 +107,15 @@
 
 	@Test
 	public void testFlowScenario10() {
+		analyzer.visitTryCatchBlock(new Label(), new Label(), label,
+				"java/lang/Exception");
+		analyzer.visitJumpInsn(GOTO, label);
+		assertTrue(LabelInfo.isMultiTarget(label));
+		assertFalse(LabelInfo.isSuccessor(label));
+	}
+
+	@Test
+	public void testFlowScenario11() {
 		// Even if the same label is referenced multiple times but from the same
 		// source instruction this is only counted as one target.
 		analyzer.visitLookupSwitchInsn(label, new int[] { 0, 1 }, new Label[] {
@@ -116,7 +125,7 @@
 	}
 
 	@Test
-	public void testFlowScenario11() {
+	public void testFlowScenario12() {
 		// Even if the same label is referenced multiple times but from the same
 		// source instruction this is only counted as one target.
 		analyzer.visitTableSwitchInsn(0, 1, label, new Label[] { label, label });
diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/flow/MethodProbesAdapterTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/flow/MethodProbesAdapterTest.java
index af5e98b..25da70c 100644
--- a/org.jacoco.core.test/src/org/jacoco/core/internal/flow/MethodProbesAdapterTest.java
+++ b/org.jacoco.core.test/src/org/jacoco/core/internal/flow/MethodProbesAdapterTest.java
@@ -330,6 +330,7 @@
 	public void testVisitTryCatchBlockWithProbe() {
 		Label target = new Label();
 		LabelInfo.setSuccessor(target);
+		LabelInfo.setTarget(target);
 		Label end = new Label();
 		Label handler = new Label();
 		Label start = new Label();
@@ -348,6 +349,7 @@
 	public void testVisitMultipleTryCatchBlocksWithProbe() {
 		Label target = new Label();
 		LabelInfo.setSuccessor(target);
+		LabelInfo.setTarget(target);
 		Label end = new Label();
 		Label handler1 = new Label();
 		Label handler2 = new Label();
diff --git a/org.jacoco.core.test/src/org/jacoco/core/test/validation/ExceptionsTest.java b/org.jacoco.core.test/src/org/jacoco/core/test/validation/ExceptionsTest.java
index a3bfa81..0351782 100644
--- a/org.jacoco.core.test/src/org/jacoco/core/test/validation/ExceptionsTest.java
+++ b/org.jacoco.core.test/src/org/jacoco/core/test/validation/ExceptionsTest.java
@@ -61,7 +61,17 @@
 		assertLine("implicitExceptionTryCatch.catchBlock",
 				ICounter.FULLY_COVERED);
 
-		// 5. Try/Catch Block With Exception Thrown Explicitly
+		// 5. Try/Catch Block With Exception Thrown Implicitly After Condition
+		// As the try/catch block is entered at one branch of the condition
+		// should be marked as executed
+		assertLine("implicitExceptionTryCatchAfterCondition.condition",
+				ICounter.FULLY_COVERED, 1, 1);
+		assertLine("implicitExceptionTryCatchAfterCondition.exception",
+				ICounter.NOT_COVERED);
+		assertLine("implicitExceptionTryCatchAfterCondition.catchBlock",
+				ICounter.FULLY_COVERED);
+
+		// 6. Try/Catch Block With Exception Thrown Explicitly
 		assertLine("explicitExceptionTryCatch.beforeBlock",
 				ICounter.FULLY_COVERED);
 		assertLine("explicitExceptionTryCatch.before", ICounter.FULLY_COVERED);
@@ -69,13 +79,13 @@
 		assertLine("explicitExceptionTryCatch.catchBlock",
 				ICounter.FULLY_COVERED);
 
-		// 6. Finally Block Without Exception Thrown
+		// 7. Finally Block Without Exception Thrown
 		// Finally block is yellow as the exception path is missing.
 		assertLine("noExceptionFinally.beforeBlock", ICounter.FULLY_COVERED);
 		assertLine("noExceptionFinally.tryBlock", ICounter.FULLY_COVERED);
 		assertLine("noExceptionFinally.finallyBlock", ICounter.PARTLY_COVERED);
 
-		// 7. Finally Block With Implicit Exception
+		// 8. Finally Block With Implicit Exception
 		// Finally block is yellow as the non-exception path is missing.
 		assertLine("implicitExceptionFinally.beforeBlock",
 				ICounter.FULLY_COVERED);
@@ -85,7 +95,7 @@
 		assertLine("implicitExceptionFinally.finallyBlock",
 				ICounter.PARTLY_COVERED);
 
-		// 8. Finally Block With Exception Thrown Explicitly
+		// 9. Finally Block With Exception Thrown Explicitly
 		assertLine("explicitExceptionFinally.beforeBlock",
 				ICounter.FULLY_COVERED);
 		assertLine("explicitExceptionFinally.before", ICounter.FULLY_COVERED);
diff --git a/org.jacoco.core.test/src/org/jacoco/core/test/validation/targets/Target03.java b/org.jacoco.core.test/src/org/jacoco/core/test/validation/targets/Target03.java
index 0ea2d69..41dfbe0 100644
--- a/org.jacoco.core.test/src/org/jacoco/core/test/validation/targets/Target03.java
+++ b/org.jacoco.core.test/src/org/jacoco/core/test/validation/targets/Target03.java
@@ -12,6 +12,7 @@
 package org.jacoco.core.test.validation.targets;
 
 import static org.jacoco.core.test.validation.targets.Stubs.ex;
+import static org.jacoco.core.test.validation.targets.Stubs.f;
 import static org.jacoco.core.test.validation.targets.Stubs.nop;
 
 import org.jacoco.core.test.validation.targets.Stubs.StubException;
@@ -32,6 +33,7 @@
 		}
 		noExceptionTryCatch();
 		implicitExceptionTryCatch();
+		implicitExceptionTryCatchAfterCondition();
 		explicitExceptionTryCatch();
 		noExceptionFinally();
 		try {
@@ -75,6 +77,17 @@
 		}
 	}
 
+	private void implicitExceptionTryCatchAfterCondition() {
+		if (f()) { // $line-implicitExceptionTryCatchAfterCondition.condition$
+			return;
+		}
+		try {
+			ex(); // $line-implicitExceptionTryCatchAfterCondition.exception$
+		} catch (StubException e) {
+			nop(); // $line-implicitExceptionTryCatchAfterCondition.catchBlock$
+		}
+	}
+
 	private void explicitExceptionTryCatch() {
 		nop(); // $line-explicitExceptionTryCatch.beforeBlock$
 		try {
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/flow/LabelFlowAnalyzer.java b/org.jacoco.core/src/org/jacoco/core/internal/flow/LabelFlowAnalyzer.java
index ad1e740..08176df 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/flow/LabelFlowAnalyzer.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/flow/LabelFlowAnalyzer.java
@@ -32,7 +32,13 @@
 	 *            Method to mark labels
 	 */
 	public static void markLabels(final MethodNode method) {
-		method.instructions.accept(new LabelFlowAnalyzer());
+		// We do not use the accept() method as ASM resets labels after every
+		// call to accept()
+		final MethodVisitor lfa = new LabelFlowAnalyzer();
+		for (int i = method.tryCatchBlocks.size(); --i >= 0;) {
+			method.tryCatchBlocks.get(i).accept(lfa);
+		}
+		method.instructions.accept(lfa);
 	}
 
 	/**
@@ -55,6 +61,19 @@
 	}
 
 	@Override
+	public void visitTryCatchBlock(final Label start, final Label end,
+			final Label handler, final String type) {
+		// Enforce probe at the beginning of the block. Assuming the start of
+		// the block already is successor of some other code, adding a target
+		// makes the start a multitarget. However, if the start of the block
+		// also is the start of the method, no probe will be added.
+		LabelInfo.setTarget(start);
+
+		// Mark exception handler as possible target of the block
+		LabelInfo.setTarget(handler);
+	}
+
+	@Override
 	public void visitJumpInsn(final int opcode, final Label label) {
 		LabelInfo.setTarget(label);
 		if (opcode == Opcodes.JSR) {
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/flow/MethodProbesAdapter.java b/org.jacoco.core/src/org/jacoco/core/internal/flow/MethodProbesAdapter.java
index 135e3eb..32ef9f1 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/flow/MethodProbesAdapter.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/flow/MethodProbesAdapter.java
@@ -64,13 +64,12 @@
 	@Override
 	public void visitTryCatchBlock(Label start, final Label end,
 			final Label handler, final String type) {
-		// If it is not the direct start of a method we insert an extra probe at
-		// the beginning of every try/catch block. To ensure that the probe
-		// itsel is still within the try/catch block, we introduce a new start
-		// label.
+		// If a probe will be inserted before the start label, we'll need to use
+		// a different label for the try-catch block.
 		if (tryCatchProbeLabels.containsKey(start)) {
 			start = tryCatchProbeLabels.get(start);
-		} else if (LabelInfo.isSuccessor(start)) {
+		} else if (LabelInfo.isMultiTarget(start)
+				&& LabelInfo.isSuccessor(start)) {
 			final Label probeLabel = new Label();
 			LabelInfo.setSuccessor(probeLabel);
 			tryCatchProbeLabels.put(start, probeLabel);
@@ -81,14 +80,11 @@
 
 	@Override
 	public void visitLabel(final Label label) {
-		final Label tryCatchStartLabel = tryCatchProbeLabels.get(label);
-		if (tryCatchStartLabel != null) {
-			probesVisitor.visitLabel(tryCatchStartLabel);
-			probesVisitor.visitProbe(idGenerator.nextId());
-		} else {
-			if (LabelInfo.isMultiTarget(label) && LabelInfo.isSuccessor(label)) {
-				probesVisitor.visitProbe(idGenerator.nextId());
+		if (LabelInfo.isMultiTarget(label) && LabelInfo.isSuccessor(label)) {
+			if (tryCatchProbeLabels.containsKey(label)) {
+				probesVisitor.visitLabel(tryCatchProbeLabels.get(label));
 			}
+			probesVisitor.visitProbe(idGenerator.nextId());
 		}
 		probesVisitor.visitLabel(label);
 	}
diff --git a/org.jacoco.doc/docroot/doc/changes.html b/org.jacoco.doc/docroot/doc/changes.html
index c4f3ffa..0cbcdda 100644
--- a/org.jacoco.doc/docroot/doc/changes.html
+++ b/org.jacoco.doc/docroot/doc/changes.html
@@ -20,6 +20,14 @@
 
 <h2>Snapshot Build @qualified.bundle.version@ (@build.date@)</h2>
 
+<h3>Fixed Bugs</h3>
+<ul>
+  <li>Restored exec file compatibility with version 0.7.2 by fixing missing
+      probes in case of try/catch blocks which are jump targets.
+      (GitHub <a href="https://github.com/jacoco/jacoco/issues/286">#286</a>).</li>
+</ul>
+
+
 <h2>Release 0.7.3 (2015/02/19)</h2>
 
 <h3>New Features</h3>