Improve coverage data for code with implicit exception.

Add additional probe before every line with at least one method
invocation.
diff --git a/jacoco-maven-plugin.test/it/it-report-nomatch/nomatch.exec b/jacoco-maven-plugin.test/it/it-report-nomatch/nomatch.exec
index 31cad96..b339652 100644
--- a/jacoco-maven-plugin.test/it/it-report-nomatch/nomatch.exec
+++ b/jacoco-maven-plugin.test/it/it-report-nomatch/nomatch.exec
Binary files differ
diff --git a/org.jacoco.ant.test/src/org/jacoco/ant/CreateExecFiles.java b/org.jacoco.ant.test/src/org/jacoco/ant/CreateExecFiles.java
new file mode 100644
index 0000000..cbe9f90
--- /dev/null
+++ b/org.jacoco.ant.test/src/org/jacoco/ant/CreateExecFiles.java
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2015 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.ant;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.jacoco.core.data.ExecutionData;
+import org.jacoco.core.data.ExecutionDataWriter;
+
+/**
+ * Utility class to create exec files required for some Ant tests.
+ */
+public class CreateExecFiles {
+
+	private static final String BASE_LOCATION = "./src/org/jacoco/ant/data/";
+
+	public static void main(String[] args) throws IOException {
+		OutputStream out;
+
+		out = new FileOutputStream(BASE_LOCATION + "sample1.exec");
+		new ExecutionDataWriter(out);
+		out.close();
+
+		out = new FileOutputStream(BASE_LOCATION + "sample2.exec");
+		new ExecutionDataWriter(out);
+		out.close();
+
+		out = new FileOutputStream(BASE_LOCATION + "nomatch.exec");
+		ExecutionDataWriter writer = new ExecutionDataWriter(out);
+		writer.visitClassExecution(new ExecutionData(0,
+				"org/jacoco/ant/TestTarget", new boolean[0]));
+		out.close();
+	}
+
+}
diff --git a/org.jacoco.ant.test/src/org/jacoco/ant/InstrumentTaskTest.xml b/org.jacoco.ant.test/src/org/jacoco/ant/InstrumentTaskTest.xml
index 92cef76..79a21ba 100644
--- a/org.jacoco.ant.test/src/org/jacoco/ant/InstrumentTaskTest.xml
+++ b/org.jacoco.ant.test/src/org/jacoco/ant/InstrumentTaskTest.xml
@@ -64,7 +64,7 @@
 		<jacoco:instrument destdir="${instr.dir}">
 			<fileset dir="${lib.dir}" includes="*.jar"/>
 		</jacoco:instrument>
-		<au:assertLogContains text="Instrumented 14 classes to ${temp.dir}"/>
+		<au:assertLogContains text="Instrumented 15 classes to ${temp.dir}"/>
 
 		<unzip src="${instr.dir}/test.jar" dest="${instr.dir}"/>
 		<au:assertFileDoesntExist file="${instr.dir}/META-INF/TEST.RSA" />
@@ -85,7 +85,7 @@
 		<jacoco:instrument destdir="${instr.dir}" removesignatures="false">
 			<fileset dir="${lib.dir}" includes="*.jar"/>
 		</jacoco:instrument>
-		<au:assertLogContains text="Instrumented 14 classes to ${temp.dir}"/>
+		<au:assertLogContains text="Instrumented 15 classes to ${temp.dir}"/>
 
 		<unzip src="${instr.dir}/test.jar" dest="${instr.dir}"/>
 		<au:assertFileExists file="${instr.dir}/META-INF/TEST.RSA" />
@@ -96,7 +96,7 @@
 		<jacoco:instrument destdir="${temp.dir}">
 			<fileset dir="${org.jacoco.ant.instrumentTaskTest.classes.dir}" includes="**/*.class"/>
 		</jacoco:instrument>
-		<au:assertLogContains text="Instrumented 14 classes to ${temp.dir}"/>
+		<au:assertLogContains text="Instrumented 15 classes to ${temp.dir}"/>
 		<au:assertFileExists file="${temp.dir}/org/jacoco/ant/InstrumentTaskTest.class" />
 
 		<echo file="${temp.dir}/jacoco-agent.properties">destfile=test.exec</echo>
@@ -113,7 +113,7 @@
 		<jacoco:instrument destdir="${temp.dir}">
 			<fileset dir="${org.jacoco.ant.instrumentTaskTest.classes.dir}" includes="**/*.class"/>
 		</jacoco:instrument>
-		<au:assertLogContains text="Instrumented 14 classes to ${temp.dir}"/>
+		<au:assertLogContains text="Instrumented 15 classes to ${temp.dir}"/>
 		<au:assertFileExists file="${temp.dir}/org/jacoco/ant/InstrumentTaskTest.class" />
 
 		<java classname="org.jacoco.ant.TestTarget" failonerror="true" fork="true">
diff --git a/org.jacoco.ant.test/src/org/jacoco/ant/data/nomatch.exec b/org.jacoco.ant.test/src/org/jacoco/ant/data/nomatch.exec
index ef7d62a..66d78df 100644
--- a/org.jacoco.ant.test/src/org/jacoco/ant/data/nomatch.exec
+++ b/org.jacoco.ant.test/src/org/jacoco/ant/data/nomatch.exec
Binary files differ
diff --git a/org.jacoco.ant.test/src/org/jacoco/ant/data/sample1.exec b/org.jacoco.ant.test/src/org/jacoco/ant/data/sample1.exec
index b9c3ab9..2e87d6c 100644
--- a/org.jacoco.ant.test/src/org/jacoco/ant/data/sample1.exec
+++ b/org.jacoco.ant.test/src/org/jacoco/ant/data/sample1.exec
@@ -1 +1 @@
-ÀÀ
\ No newline at end of file
+ÀÀ
\ No newline at end of file
diff --git a/org.jacoco.ant.test/src/org/jacoco/ant/data/sample2.exec b/org.jacoco.ant.test/src/org/jacoco/ant/data/sample2.exec
index b9c3ab9..2e87d6c 100644
--- a/org.jacoco.ant.test/src/org/jacoco/ant/data/sample2.exec
+++ b/org.jacoco.ant.test/src/org/jacoco/ant/data/sample2.exec
@@ -1 +1 @@
-ÀÀ
\ No newline at end of file
+ÀÀ
\ No newline at end of file
diff --git a/org.jacoco.core.test/src/org/jacoco/core/analysis/AnalyzerTest.java b/org.jacoco.core.test/src/org/jacoco/core/analysis/AnalyzerTest.java
index f63762e..6a9275e 100644
--- a/org.jacoco.core.test/src/org/jacoco/core/analysis/AnalyzerTest.java
+++ b/org.jacoco.core.test/src/org/jacoco/core/analysis/AnalyzerTest.java
@@ -93,7 +93,7 @@
 		final byte[] bytes = TargetLoader
 				.getClassDataAsBytes(AnalyzerTest.class);
 		executionData.get(Long.valueOf(CRC64.checksum(bytes)),
-				"org/jacoco/core/analysis/AnalyzerTest", 100);
+				"org/jacoco/core/analysis/AnalyzerTest", 200);
 		analyzer.analyzeClass(bytes, "Test");
 		assertFalse(classes.get("org/jacoco/core/analysis/AnalyzerTest")
 				.isNoMatch());
@@ -102,7 +102,7 @@
 	@Test
 	public void testAnalyzeClassNoIdMatch() throws IOException {
 		executionData.get(Long.valueOf(0),
-				"org/jacoco/core/analysis/AnalyzerTest", 100);
+				"org/jacoco/core/analysis/AnalyzerTest", 200);
 		analyzer.analyzeClass(
 				TargetLoader.getClassDataAsBytes(AnalyzerTest.class), "Test");
 		assertTrue(classes.get("org/jacoco/core/analysis/AnalyzerTest")
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 440b4f3..8081c67 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
@@ -12,6 +12,8 @@
 package org.jacoco.core.internal.flow;
 
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 import static org.objectweb.asm.Opcodes.*;
 
@@ -36,6 +38,13 @@
 	}
 
 	@Test
+	public void testInit() {
+		assertFalse(analyzer.successor);
+		assertTrue(analyzer.first);
+		assertNull(analyzer.lineStart);
+	}
+
+	@Test
 	public void testFlowScenario01() {
 		assertFalse(LabelInfo.isMultiTarget(label));
 		assertFalse(LabelInfo.isSuccessor(label));
@@ -134,12 +143,6 @@
 	}
 
 	@Test
-	public void testInit() {
-		assertFalse(analyzer.successor);
-		assertTrue(analyzer.first);
-	}
-
-	@Test
 	public void testInsn() {
 		testInsn(NOP, true);
 		testInsn(ACONST_NULL, true);
@@ -294,17 +297,27 @@
 	}
 
 	@Test
+	public void testLineNumber() {
+		analyzer.visitLineNumber(42, label);
+		assertSame(label, analyzer.lineStart);
+	}
+
+	@Test
 	public void testMethodInsn() {
+		analyzer.visitLineNumber(42, label);
 		analyzer.visitMethodInsn(INVOKEVIRTUAL, "Foo", "doit", "()V", false);
 		assertTrue(analyzer.successor);
 		assertFalse(analyzer.first);
+		assertTrue(LabelInfo.isMethodInvocationLine(label));
 	}
 
 	@Test
 	public void testInvokeDynamicInsn() {
+		analyzer.visitLineNumber(42, label);
 		analyzer.visitInvokeDynamicInsn("foo", "()V", null);
 		assertTrue(analyzer.successor);
 		assertFalse(analyzer.first);
+		assertTrue(LabelInfo.isMethodInvocationLine(label));
 	}
 
 	@Test
diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/flow/LabelInfoTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/flow/LabelInfoTest.java
index c5d74a6..d83bee6 100644
--- a/org.jacoco.core.test/src/org/jacoco/core/internal/flow/LabelInfoTest.java
+++ b/org.jacoco.core.test/src/org/jacoco/core/internal/flow/LabelInfoTest.java
@@ -37,6 +37,7 @@
 	public void testDefaults() {
 		assertFalse(LabelInfo.isMultiTarget(label));
 		assertFalse(LabelInfo.isSuccessor(label));
+		assertFalse(LabelInfo.isMethodInvocationLine(label));
 		assertFalse(LabelInfo.isDone(label));
 		assertEquals(LabelInfo.NO_PROBE, LabelInfo.getProbeId(label));
 		assertNull(LabelInfo.getIntermediateLabel(label));
@@ -90,6 +91,42 @@
 	}
 
 	@Test
+	public void testMethodInvocationLine() {
+		LabelInfo.setMethodInvocationLine(label);
+		assertTrue(LabelInfo.isMethodInvocationLine(label));
+	}
+
+	@Test
+	public void testNeedsProbe() {
+		testNeedsProbe(false, false, false, false);
+		testNeedsProbe(true, false, false, false);
+		testNeedsProbe(false, true, false, false);
+		testNeedsProbe(true, true, false, false);
+		testNeedsProbe(false, false, true, false);
+		testNeedsProbe(true, false, true, true);
+		testNeedsProbe(false, true, true, true);
+		testNeedsProbe(true, true, true, true);
+	}
+
+	private void testNeedsProbe(boolean multitarget,
+			boolean methodinvocationline, boolean successor, boolean expected) {
+		if (multitarget) {
+			LabelInfo.setTarget(label);
+			LabelInfo.setTarget(label);
+		}
+		if (methodinvocationline) {
+			LabelInfo.setMethodInvocationLine(label);
+		}
+		if (successor) {
+			LabelInfo.setSuccessor(label);
+		}
+		assertTrue(expected == LabelInfo.needsProbe(label));
+
+		// Reset:
+		label = new Label();
+	}
+
+	@Test
 	public void testSetResetDone1() {
 		LabelInfo.setDone(label);
 		assertTrue(LabelInfo.isDone(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 0351782..4008091 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
@@ -36,7 +36,7 @@
 		// 1. Implicit Exception
 		// Currently no coverage at all, as we don't see when a block aborts
 		// somewhere in the middle.
-		assertLine("implicitException.before", ICounter.NOT_COVERED);
+		assertLine("implicitException.before", ICounter.FULLY_COVERED);
 		assertLine("implicitException.exception", ICounter.NOT_COVERED);
 		assertLine("implicitException.after", ICounter.NOT_COVERED);
 
@@ -55,7 +55,7 @@
 		// somewhere in the middle.
 		assertLine("implicitExceptionTryCatch.beforeBlock",
 				ICounter.FULLY_COVERED);
-		assertLine("implicitExceptionTryCatch.before", ICounter.NOT_COVERED);
+		assertLine("implicitExceptionTryCatch.before", ICounter.FULLY_COVERED);
 		assertLine("implicitExceptionTryCatch.exception", ICounter.NOT_COVERED);
 		assertLine("implicitExceptionTryCatch.after", ICounter.NOT_COVERED);
 		assertLine("implicitExceptionTryCatch.catchBlock",
@@ -89,7 +89,7 @@
 		// Finally block is yellow as the non-exception path is missing.
 		assertLine("implicitExceptionFinally.beforeBlock",
 				ICounter.FULLY_COVERED);
-		assertLine("implicitExceptionFinally.before", ICounter.NOT_COVERED);
+		assertLine("implicitExceptionFinally.before", ICounter.FULLY_COVERED);
 		assertLine("implicitExceptionFinally.exception", ICounter.NOT_COVERED);
 		assertLine("implicitExceptionFinally.after", ICounter.NOT_COVERED);
 		assertLine("implicitExceptionFinally.finallyBlock",
diff --git a/org.jacoco.core/src/org/jacoco/core/data/ExecutionDataWriter.java b/org.jacoco.core/src/org/jacoco/core/data/ExecutionDataWriter.java
index d986e53..1095d95 100644
--- a/org.jacoco.core/src/org/jacoco/core/data/ExecutionDataWriter.java
+++ b/org.jacoco.core/src/org/jacoco/core/data/ExecutionDataWriter.java
@@ -24,7 +24,7 @@
 		IExecutionDataVisitor {
 
 	/** File format version, will be incremented for each incompatible change. */
-	public static final char FORMAT_VERSION = 0x1006;
+	public static final char FORMAT_VERSION = 0x1007;
 
 	/** Magic number in header for file format identification. */
 	public static final char MAGIC_NUMBER = 0xC0C0;
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 08176df..157416a 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
@@ -54,6 +54,11 @@
 	boolean first = true;
 
 	/**
+	 * Label instance of the last line start.
+	 */
+	Label lineStart = null;
+
+	/**
 	 * Create new instance.
 	 */
 	public LabelFlowAnalyzer() {
@@ -94,6 +99,11 @@
 	}
 
 	@Override
+	public void visitLineNumber(final int line, final Label start) {
+		lineStart = start;
+	}
+
+	@Override
 	public void visitTableSwitchInsn(final int min, final int max,
 			final Label dflt, final Label... labels) {
 		visitSwitchInsn(dflt, labels);
@@ -174,6 +184,7 @@
 			final String name, final String desc, final boolean itf) {
 		successor = true;
 		first = false;
+		markMethodInvocationLine();
 	}
 
 	@Override
@@ -181,6 +192,13 @@
 			final Handle bsm, final Object... bsmArgs) {
 		successor = true;
 		first = false;
+		markMethodInvocationLine();
+	}
+
+	private void markMethodInvocationLine() {
+		if (lineStart != null) {
+			LabelInfo.setMethodInvocationLine(lineStart);
+		}
 	}
 
 	@Override
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/flow/LabelInfo.java b/org.jacoco.core/src/org/jacoco/core/internal/flow/LabelInfo.java
index 956761d..c6f1b76 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/flow/LabelInfo.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/flow/LabelInfo.java
@@ -31,6 +31,8 @@
 
 	private boolean successor = false;
 
+	private boolean methodInvocationLine = false;
+
 	private boolean done = false;
 
 	private int probeid = NO_PROBE;
@@ -105,6 +107,43 @@
 	}
 
 	/**
+	 * Mark a given label as the beginning of a line with method invocations.
+	 * 
+	 * @param label
+	 *            label to mark
+	 */
+	public static void setMethodInvocationLine(final Label label) {
+		create(label).methodInvocationLine = true;
+	}
+
+	/**
+	 * Checks whether the a given label has been marked as a line with method
+	 * invocations.
+	 * 
+	 * @param label
+	 *            label to check
+	 * @return <code>true</code> if the label represents a line with method
+	 *         invocations
+	 */
+	public static boolean isMethodInvocationLine(final Label label) {
+		final LabelInfo info = get(label);
+		return info == null ? false : info.methodInvocationLine;
+	}
+
+	/**
+	 * Determines whether the given label needs a probe to be inserted before.
+	 * 
+	 * @param label
+	 *            label to test
+	 * @return <code>true</code> if a probe should be inserted before
+	 */
+	public static boolean needsProbe(final Label label) {
+		final LabelInfo info = get(label);
+		return info != null && info.successor
+				&& (info.multiTarget || info.methodInvocationLine);
+	}
+
+	/**
 	 * Mark a given label as done.
 	 * 
 	 * @param label
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 32ef9f1..5081fff 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
@@ -68,8 +68,7 @@
 		// a different label for the try-catch block.
 		if (tryCatchProbeLabels.containsKey(start)) {
 			start = tryCatchProbeLabels.get(start);
-		} else if (LabelInfo.isMultiTarget(start)
-				&& LabelInfo.isSuccessor(start)) {
+		} else if (LabelInfo.needsProbe(start)) {
 			final Label probeLabel = new Label();
 			LabelInfo.setSuccessor(probeLabel);
 			tryCatchProbeLabels.put(start, probeLabel);
@@ -80,7 +79,7 @@
 
 	@Override
 	public void visitLabel(final Label label) {
-		if (LabelInfo.isMultiTarget(label) && LabelInfo.isSuccessor(label)) {
+		if (LabelInfo.needsProbe(label)) {
 			if (tryCatchProbeLabels.containsKey(label)) {
 				probesVisitor.visitLabel(tryCatchProbeLabels.get(label));
 			}
diff --git a/org.jacoco.doc/docroot/doc/changes.html b/org.jacoco.doc/docroot/doc/changes.html
index 458f858..2418207 100644
--- a/org.jacoco.doc/docroot/doc/changes.html
+++ b/org.jacoco.doc/docroot/doc/changes.html
@@ -22,6 +22,8 @@
 
 <h3>New Features</h3>
 <ul>
+  <li>Better detection of coverage in code blocks with implicit exceptions.
+      (GitHub <a href="https://github.com/jacoco/jacoco/issues/310">#310</a>).</li>
   <li>Added lifecycle-mapping-metadata.xml for M2E
       (GitHub <a href="https://github.com/jacoco/jacoco/issues/203">#203</a>).</li>
   <li>Allow locales with country and variant for Ant report task
@@ -36,6 +38,12 @@
       (GitHub <a href="https://github.com/jacoco/jacoco/issues/301">#301</a>).</li>
 </ul>
 
+<h3>API Changes</h3>
+<ul>
+  <li>The exec file version has been updated and is not compatible with previous
+      versions.</li>
+</ul>
+
 <h2>Release 0.7.4 (2015/02/26)</h2>
 
 <h3>Fixed Bugs</h3>
diff --git a/org.jacoco.doc/docroot/doc/faq.html b/org.jacoco.doc/docroot/doc/faq.html
index 1748b4d..62d8114 100644
--- a/org.jacoco.doc/docroot/doc/faq.html
+++ b/org.jacoco.doc/docroot/doc/faq.html
@@ -45,13 +45,13 @@
   duplicate classes or create separate reports or report groups for each version.
 </p>
 
-<h3>Code with exceptions shows no coverage. Why?</h3>
+<h3>Source code lines with exceptions show no coverage. Why?</h3>
 <p>
   JaCoCo determines code execution with so called probes. Probes are inserted
   into the control flow at certain positions. Code is considered as executed
   when a subsequent probe has been executed. In case of exceptions such a
-  sequence of instructions is aborted somewhere in the middle and not marked as
-  executed.
+  sequence of instructions is aborted somewhere in the middle and the
+  corresponding line of source code is not marked as covered.
 </p>
 
 <h3>Why does the coverage report not show line coverage figures?</h3>
diff --git a/org.jacoco.doc/docroot/doc/flow.html b/org.jacoco.doc/docroot/doc/flow.html
index 8eec88b..98678bb 100644
--- a/org.jacoco.doc/docroot/doc/flow.html
+++ b/org.jacoco.doc/docroot/doc/flow.html
@@ -257,6 +257,27 @@
   above.
 </p>
 
+<h2>Additional Probes Between Lines</h2>
+
+<p>
+  The probe insertion strategy described so far does not consider implicit
+  exceptions thrown for example from invoked methods. If the control flow
+  between two probes is interrupted by a exception not explicitly created with
+  a <code>throw</code> statement all instruction in between are considered as
+  not covered. This leads to unexpected results especially when the the block of
+  instructions spans multiple lines of source code.
+</p>
+
+<p>
+  Therefore JaCoCo adds an additional probe between the instructions of two
+  lines whenever the subsequent line contains at least one method invocation.
+  This limits the effect of implicit exceptions from method invocations to
+  single lines of source. The approach only works for class files compiled with
+  debug information (line numbers) and does not consider implicit exceptions
+  from other instructions than method invocations (e.g.
+  <code>NullPointerException</code> or <code>ArrayIndexOutOfBoundsException</code>).
+</p>
+
 <h2>Probe Implementation</h2>
 
 <p>