aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEvgeny Mandrikov <138671+Godin@users.noreply.github.com>2024-05-12 18:45:47 +0200
committerGitHub <noreply@github.com>2024-05-12 18:45:47 +0200
commitadf9595df74ad37f0c9c00c9a54942e326e875df (patch)
treea6c513a3c1e51a5a48f4feb9eb018f658be6ac25
parent3bba375e0cb6416efa1a3e3682a7d83cc75a3da8 (diff)
downloadjacoco-upstream-master.tar.gz
Add filter for Kotlin inline value classes (#1475)upstream-master
-rw-r--r--org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinInlineClassTest.java27
-rw-r--r--org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinInlineClassTarget.kt75
-rw-r--r--org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinInlineClassFilterTest.java98
-rw-r--r--org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/Filters.java1
-rw-r--r--org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinInlineClassFilter.java89
-rw-r--r--org.jacoco.doc/docroot/doc/changes.html3
6 files changed, 293 insertions, 0 deletions
diff --git a/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinInlineClassTest.java b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinInlineClassTest.java
new file mode 100644
index 00000000..908ad782
--- /dev/null
+++ b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinInlineClassTest.java
@@ -0,0 +1,27 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2024 Mountainminds GmbH & Co. KG and Contributors
+ * This program and the accompanying materials are made available under
+ * the terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Evgeny Mandrikov - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.core.test.validation.kotlin;
+
+import org.jacoco.core.test.validation.ValidationTestBase;
+import org.jacoco.core.test.validation.kotlin.targets.KotlinInlineClassTarget;
+
+/**
+ * Test of code coverage in {@link KotlinInlineClassTarget}.
+ */
+public class KotlinInlineClassTest extends ValidationTestBase {
+
+ public KotlinInlineClassTest() {
+ super(KotlinInlineClassTarget.class);
+ }
+
+}
diff --git a/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinInlineClassTarget.kt b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinInlineClassTarget.kt
new file mode 100644
index 00000000..079fa6d9
--- /dev/null
+++ b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinInlineClassTarget.kt
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2024 Mountainminds GmbH & Co. KG and Contributors
+ * This program and the accompanying materials are made available under
+ * the terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Evgeny Mandrikov - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.core.test.validation.kotlin.targets
+
+import org.jacoco.core.test.validation.targets.Stubs.nop
+
+/**
+ * Test target for `inline class`.
+ */
+object KotlinInlineClassTarget {
+
+ interface Base {
+ fun base()
+ }
+
+ @JvmInline
+ value class I1(val value: String) : Base { // assertEmpty()
+
+ init { // assertEmpty()
+ nop() // assertFullyCovered()
+ } // assertEmpty()
+
+ constructor() : this("") { // assertFullyCovered()
+ nop() // assertFullyCovered()
+ } // assertEmpty()
+
+ val length: Int // assertEmpty()
+ get() = value.length // assertFullyCovered()
+
+ fun f(p: String) { // assertEmpty()
+ nop(p) // assertFullyCovered()
+ } // assertFullyCovered()
+
+ fun f(p: I1) { // assertEmpty()
+ nop(p) // assertFullyCovered()
+ } // assertFullyCovered()
+
+ override fun base() { // assertEmpty()
+ nop() // assertFullyCovered()
+ } // assertFullyCovered()
+
+ } // assertEmpty()
+
+ @JvmInline
+ value class I2(val value: String) { // assertEmpty()
+
+ override fun toString(): String { // assertEmpty()
+ return "Value: $value" // assertNotCovered()
+ } // assertEmpty()
+
+ } // assertEmpty()
+
+ @JvmStatic
+ fun main(args: Array<String>) {
+ val i = I1()
+ i.value
+ i.length
+ i.f("")
+ i.f(i)
+ i.base()
+
+ I2("")
+ }
+
+}
diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinInlineClassFilterTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinInlineClassFilterTest.java
new file mode 100644
index 00000000..8fd77a0a
--- /dev/null
+++ b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinInlineClassFilterTest.java
@@ -0,0 +1,98 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2024 Mountainminds GmbH & Co. KG and Contributors
+ * This program and the accompanying materials are made available under
+ * the terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Evgeny Mandrikov - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.core.internal.analysis.filter;
+
+import org.junit.Test;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.tree.MethodNode;
+
+/**
+ * Unit tests for {@link KotlinInlineClassFilter}.
+ */
+public class KotlinInlineClassFilterTest extends FilterTestBase {
+
+ private final IFilter filter = new KotlinInlineClassFilter();
+
+ /**
+ * <pre>
+ * &#064;kotlin.jvm.JvmInline
+ * value class Example(val value: String)
+ * </pre>
+ */
+ @Test
+ public void should_filter() {
+ context.classAnnotations
+ .add(KotlinGeneratedFilter.KOTLIN_METADATA_DESC);
+ context.classAnnotations.add("Lkotlin/jvm/JvmInline;");
+ final MethodNode m = new MethodNode(0, "getValue",
+ "()Ljava/lang/String;", null, null);
+ m.visitInsn(Opcodes.NOP);
+
+ filter.filter(m, context, output);
+
+ assertMethodIgnored(m);
+ }
+
+ /**
+ * <pre>
+ * &#064;kotlin.jvm.JvmInline
+ * value class Example(val value: String) {
+ * fun f() { ... }
+ * }
+ * </pre>
+ */
+ @Test
+ public void should_not_filter_static() {
+ context.classAnnotations
+ .add(KotlinGeneratedFilter.KOTLIN_METADATA_DESC);
+ context.classAnnotations.add("Lkotlin/jvm/JvmInline;");
+ final MethodNode m = new MethodNode(Opcodes.ACC_STATIC, "f-impl", "()V",
+ null, null);
+ m.visitInsn(Opcodes.NOP);
+
+ filter.filter(m, context, output);
+
+ assertIgnored();
+ }
+
+ /**
+ * <pre>
+ * data class Example(val value: String)
+ * </pre>
+ */
+ @Test
+ public void should_not_filter_when_no_JvmInline_annotation() {
+ context.classAnnotations
+ .add(KotlinGeneratedFilter.KOTLIN_METADATA_DESC);
+ final MethodNode m = new MethodNode(0, "getValue",
+ "()Ljava/lang/String;", null, null);
+ m.visitInsn(Opcodes.NOP);
+
+ filter.filter(m, context, output);
+
+ assertIgnored();
+ }
+
+ @Test
+ public void should_not_filter_when_not_kotlin() {
+ context.classAnnotations.add("Lkotlin/jvm/JvmInline;");
+ final MethodNode m = new MethodNode(0, "getValue",
+ "()Ljava/lang/String;", null, null);
+ m.visitInsn(Opcodes.NOP);
+
+ filter.filter(m, context, output);
+
+ assertIgnored();
+ }
+
+}
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 07268c41..c644d9f8 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
@@ -47,6 +47,7 @@ public final class Filters implements IFilter {
new KotlinWhenStringFilter(),
new KotlinUnsafeCastOperatorFilter(),
new KotlinNotNullOperatorFilter(),
+ new KotlinInlineClassFilter(),
new KotlinDefaultArgumentsFilter(), new KotlinInlineFilter(),
new KotlinCoroutineFilter(), new KotlinDefaultMethodsFilter(),
new KotlinComposeFilter());
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinInlineClassFilter.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinInlineClassFilter.java
new file mode 100644
index 00000000..0279e047
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinInlineClassFilter.java
@@ -0,0 +1,89 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2024 Mountainminds GmbH & Co. KG and Contributors
+ * This program and the accompanying materials are made available under
+ * the terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Evgeny Mandrikov - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.core.internal.analysis.filter;
+
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.tree.MethodNode;
+
+/**
+ * Filters methods that Kotlin compiler generates for inline classes.
+ *
+ * For
+ *
+ * <pre>
+ * &#064;kotlin.jvm.JvmInline
+ * value class Example(val value: String) : Base {
+ * fun f(p: String) { ... }
+ * fun f(p: Example) { ... }
+ * override fun base() { ... }
+ * }
+ * </pre>
+ *
+ * Kotlin compiler produces
+ *
+ * <pre>
+ * &#064;kotlin.jvm.JvmInline
+ * class Example implements Base {
+ * private final String value;
+ * public String getValue() { return value; }
+ *
+ * private synthetic Example(String value) { this.value = value; }
+ *
+ * public static String constructor-impl(String value) { ... }
+ *
+ * public static void f-impl(String value, String p) { ... }
+ *
+ * public static void f-ulP-heY(String value, String p) { ... }
+ *
+ * public void base() { base-impl(value); }
+ * public static void base-impl(String value) { ... }
+ *
+ * public String toString() { return toString-impl(value); }
+ * public static String toString-impl(String value) { ... }
+ *
+ * public boolean equals(Object other) { return equals-impl(value, other); }
+ * public static boolean equals-impl(String value, Object other) { ... }
+ *
+ * public int hashCode() { return hashCode-impl(value); }
+ * public static int hashCode-impl(String value) { ... }
+ *
+ * public final synthetic String unbox-impl() { return value; }
+ * public static synthetic Example box-impl(String value) { return new Example(value); }
+ *
+ * public static equals-impl0(String value1, String value2) { ... }
+ * }
+ * </pre>
+ *
+ * Except getter all non-synthetic non-static methods delegate to corresponding
+ * static methods. Non-static methods are provided for interoperability with
+ * Java and can not be invoked from Kotlin without reflection and so should be
+ * filtered out.
+ */
+final class KotlinInlineClassFilter implements IFilter {
+
+ public void filter(final MethodNode methodNode,
+ final IFilterContext context, final IFilterOutput output) {
+ if (!KotlinGeneratedFilter.isKotlinClass(context)) {
+ return;
+ }
+ if (!context.getClassAnnotations().contains("Lkotlin/jvm/JvmInline;")) {
+ return;
+ }
+ if ((methodNode.access & Opcodes.ACC_STATIC) != 0) {
+ return;
+ }
+ output.ignore(methodNode.instructions.getFirst(),
+ methodNode.instructions.getLast());
+ }
+
+}
diff --git a/org.jacoco.doc/docroot/doc/changes.html b/org.jacoco.doc/docroot/doc/changes.html
index e5a53ba9..f958060c 100644
--- a/org.jacoco.doc/docroot/doc/changes.html
+++ b/org.jacoco.doc/docroot/doc/changes.html
@@ -25,6 +25,9 @@
<li>Part of bytecode generated by the Kotlin Compose compiler plugin is
filtered out during generation of report
(GitHub <a href="https://github.com/jacoco/jacoco/issues/1616">#1616</a>).</li>
+ <li>Part of bytecode generated by the Kotlin compiler for inline value classes is
+ filtered out during generation of report
+ (GitHub <a href="https://github.com/jacoco/jacoco/issues/1475">#1475</a>).</li>
</ul>
<h2>Release 0.8.12 (2024/03/31)</h2>