Filter branch generated by kotlinc for reading lateinit properties (#707)

diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinLateinitFilterTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinLateinitFilterTest.java
new file mode 100644
index 0000000..a2306f8
--- /dev/null
+++ b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinLateinitFilterTest.java
@@ -0,0 +1,74 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2018 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:
+ *    Fabian Mastenbroek - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.core.internal.analysis.filter;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+
+import org.jacoco.core.internal.instr.InstrSupport;
+import org.junit.Test;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.tree.AbstractInsnNode;
+import org.objectweb.asm.tree.MethodNode;
+
+public class KotlinLateinitFilterTest implements IFilterOutput {
+	private final KotlinLateinitFilter filter = new KotlinLateinitFilter();
+
+	private final MethodNode m = new MethodNode(InstrSupport.ASM_API_VERSION, 0,
+			"name", "()V", null, null);
+
+	private AbstractInsnNode fromInclusive;
+	private AbstractInsnNode toInclusive;
+
+	@Test
+	public void testLateinitBranchIsFiltered() {
+		final Label l1 = new Label();
+		final Label l2 = new Label();
+
+		m.visitLabel(l1);
+		m.visitVarInsn(Opcodes.ALOAD, 0);
+		m.visitFieldInsn(Opcodes.GETFIELD,
+				"com/better/alarm/background/VibrationService", "wakeLock",
+				"Landroid/os/PowerManager$WakeLock;");
+		m.visitInsn(Opcodes.DUP);
+		m.visitJumpInsn(Opcodes.IFNONNULL, l2);
+
+		final AbstractInsnNode expectedFrom = m.instructions.getLast();
+
+		m.visitLdcInsn("wakelock");
+		m.visitMethodInsn(Opcodes.INVOKESTATIC,
+				"kotlin/jvm/internal/Intrinsics",
+				"throwUninitializedPropertyAccessException",
+				"Ljava/lang/String;", false);
+		final AbstractInsnNode expectedTo = m.instructions.getLast();
+		m.visitLabel(l2);
+		m.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
+				"android/os/PowerManager$WakeLock", "acquire", "", false);
+		filter.filter(m, new FilterContextMock(), this);
+
+		assertEquals(expectedFrom, fromInclusive);
+		assertEquals(expectedTo, toInclusive);
+	}
+
+	public void ignore(AbstractInsnNode fromInclusive,
+			AbstractInsnNode toInclusive) {
+		assertNull(this.fromInclusive);
+		this.fromInclusive = fromInclusive;
+		this.toInclusive = toInclusive;
+	}
+
+	public void merge(final AbstractInsnNode i1, final AbstractInsnNode i2) {
+		fail();
+	}
+}
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/AbstractMatcher.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/AbstractMatcher.java
index fcd1c88..d4b421e 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/AbstractMatcher.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/AbstractMatcher.java
@@ -67,6 +67,18 @@
 		cursor = null;
 	}
 
+	final void nextIsInvokeStatic(final String owner, final String name) {
+		nextIs(Opcodes.INVOKESTATIC);
+		if (cursor == null) {
+			return;
+		}
+		final MethodInsnNode m = (MethodInsnNode) cursor;
+		if (owner.equals(m.owner) && name.equals(m.name)) {
+			return;
+		}
+		cursor = null;
+	}
+
 	final void nextIsVar(final int opcode, final String name) {
 		nextIs(opcode);
 		if (cursor == null) {
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/Filters.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/Filters.java
index cafb1f7..b235e1e 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/Filters.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/Filters.java
@@ -32,7 +32,7 @@
 			new FinallyFilter(), new PrivateEmptyNoArgConstructorFilter(),
 			new StringSwitchJavacFilter(), new LombokGeneratedFilter(),
 			new GroovyGeneratedFilter(), new EnumEmptyConstructorFilter(),
-			new KotlinGeneratedFilter());
+			new KotlinGeneratedFilter(), new KotlinLateinitFilter());
 
 	private final IFilter[] filters;
 
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinLateinitFilter.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinLateinitFilter.java
new file mode 100644
index 0000000..c1aee3a
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinLateinitFilter.java
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2018 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:
+ *    Fabian Mastenbroek - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.core.internal.analysis.filter;
+
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.tree.AbstractInsnNode;
+import org.objectweb.asm.tree.MethodNode;
+
+/**
+ * Filters branch in bytecode that Kotlin compiler generates for reading from
+ * <code>lateinit</code> properties.
+ */
+public class KotlinLateinitFilter implements IFilter {
+
+	private final static String OWNER = "kotlin/jvm/internal/Intrinsics";
+	private final static String NAME = "throwUninitializedPropertyAccessException";
+
+	public void filter(final MethodNode methodNode, final IFilterContext context,
+			final IFilterOutput output) {
+		for (AbstractInsnNode i = methodNode.instructions
+				.getFirst(); i != null; i = i.getNext()) {
+			if (i.getOpcode() != Opcodes.IFNONNULL) {
+				continue;
+			}
+
+			final AbstractInsnNode end = new Matcher(i).match();
+
+			if (end != null) {
+				output.ignore(i, end);
+			}
+		}
+	}
+
+	private static class Matcher extends AbstractMatcher {
+		private final AbstractInsnNode start;
+
+		private Matcher(final AbstractInsnNode start) {
+			this.start = start;
+		}
+
+		private AbstractInsnNode match() {
+			cursor = start;
+			nextIs(Opcodes.LDC);
+			nextIsInvokeStatic(OWNER, NAME);
+			return cursor;
+		}
+	}
+}
diff --git a/org.jacoco.doc/docroot/doc/changes.html b/org.jacoco.doc/docroot/doc/changes.html
index c5930d3..02dc8cb 100644
--- a/org.jacoco.doc/docroot/doc/changes.html
+++ b/org.jacoco.doc/docroot/doc/changes.html
@@ -27,6 +27,10 @@
   <li>Methods added by the Kotlin compiler are filtered out during generation
       of report. Idea and implementation by Nikolay Krasko
       (GitHub <a href="https://github.com/jacoco/jacoco/issues/689">#689</a>).</li>
+  <li>Branch added by the Kotlin compiler for reading from <code>lateinit</code>
+      property is filtered out during generation of report. Implementation by
+      Fabian Mastenbroek
+      (GitHub <a href="https://github.com/jacoco/jacoco/issues/707">#707</a>).</li>
 </ul>
 
 <h3>Fixed Bugs</h3>