+ *
+ * This constructor will have 8 additional parameters containing the mask.
+ *
+ * 9 additional parameters will be required for methods with more than 256
+ * parameters, but according to Java Virtual Machine Specification
+ * §4.11:
+ *
+ *
+ *
+ * The number of method parameters is limited to 255
+ *
+ *
+ */
+ @Test
+ public void should_filter_methods_with_more_than_224_parameters() {
+ final StringBuilder paramTypes = new StringBuilder();
+ for (int i = 1; i <= 225; i++) {
+ paramTypes.append("I");
+ }
+ final MethodNode m = new MethodNode(InstrSupport.ASM_API_VERSION,
+ Opcodes.ACC_SYNTHETIC, "",
+ "(" + paramTypes
+ + "IIIIIIIILkotlin/jvm/internal/DefaultConstructorMarker;)V",
+ null, null);
+ context.classAnnotations
+ .add(KotlinGeneratedFilter.KOTLIN_METADATA_DESC);
+
+ m.visitVarInsn(Opcodes.ILOAD, 226);
+ m.visitInsn(Opcodes.ICONST_1);
+ m.visitInsn(Opcodes.IAND);
+ final Label label = new Label();
+ m.visitJumpInsn(Opcodes.IFEQ, label);
+ // default argument
+ m.visitLdcInsn(Integer.valueOf(42));
+ m.visitVarInsn(Opcodes.ISTORE, 1);
+ m.visitLabel(label);
+
+ m.visitVarInsn(Opcodes.ALOAD, 0);
+ for (int i = 1; i <= 225; i++) {
+ m.visitVarInsn(Opcodes.ILOAD, i);
+ }
+ m.visitMethodInsn(Opcodes.INVOKESPECIAL, "Owner", "",
+ "(" + paramTypes + ")V", false);
+ m.visitInsn(Opcodes.RETURN);
+
+ filter.filter(m, context, output);
+
+ assertIgnored(new Range(m.instructions.get(3), m.instructions.get(3)));
+ }
+
+ @Test
+ public void computeNumberOfMaskArguments() {
+ assertEquals(1,
+ KotlinDefaultArgumentsFilter.computeNumberOfMaskArguments(3));
+ assertEquals(1,
+ KotlinDefaultArgumentsFilter.computeNumberOfMaskArguments(34));
+ assertEquals(2,
+ KotlinDefaultArgumentsFilter.computeNumberOfMaskArguments(36));
+ assertEquals(2,
+ KotlinDefaultArgumentsFilter.computeNumberOfMaskArguments(67));
+ assertEquals(3,
+ KotlinDefaultArgumentsFilter.computeNumberOfMaskArguments(69));
+ assertEquals(3,
+ KotlinDefaultArgumentsFilter.computeNumberOfMaskArguments(100));
+ assertEquals(4,
+ KotlinDefaultArgumentsFilter.computeNumberOfMaskArguments(102));
+ assertEquals(4,
+ KotlinDefaultArgumentsFilter.computeNumberOfMaskArguments(133));
+ assertEquals(5,
+ KotlinDefaultArgumentsFilter.computeNumberOfMaskArguments(135));
+ assertEquals(5,
+ KotlinDefaultArgumentsFilter.computeNumberOfMaskArguments(166));
+ assertEquals(6,
+ KotlinDefaultArgumentsFilter.computeNumberOfMaskArguments(168));
+ assertEquals(6,
+ KotlinDefaultArgumentsFilter.computeNumberOfMaskArguments(199));
+ assertEquals(7,
+ KotlinDefaultArgumentsFilter.computeNumberOfMaskArguments(201));
+ assertEquals(7,
+ KotlinDefaultArgumentsFilter.computeNumberOfMaskArguments(232));
+ assertEquals(8,
+ KotlinDefaultArgumentsFilter.computeNumberOfMaskArguments(234));
+ assertEquals(8,
+ KotlinDefaultArgumentsFilter.computeNumberOfMaskArguments(255));
+ }
+
}
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinDefaultArgumentsFilter.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinDefaultArgumentsFilter.java
index f83167c3d7..e9d53d51a9 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinDefaultArgumentsFilter.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinDefaultArgumentsFilter.java
@@ -26,8 +26,11 @@
/**
* Filters branches that Kotlin compiler generates for default arguments.
*
- * For each default argument Kotlin compiler generates following bytecode to
- * determine if it should be used or not:
+ * For methods and constructors with default arguments Kotlin compiler generates
+ * synthetic method with suffix "$default" or a synthetic constructor with last
+ * argument "kotlin.jvm.internal.DefaultConstructorMarker" respectively. And in
+ * this synthetic method for each default argument Kotlin compiler generates
+ * following bytecode to determine if it should be used or not:
*
*
* ILOAD maskVar
@@ -38,11 +41,14 @@
* label:
*
*
- * Where maskVar is penultimate argument of synthetic method with
- * suffix "$default" or of synthetic constructor with last argument
- * "kotlin.jvm.internal.DefaultConstructorMarker". And its value can't be zero -
- * invocation with all arguments uses original non synthetic method, thus
- * IFEQ instructions should be ignored.
+ * If original method has X arguments, then in synthetic method
+ * maskVar is one of arguments from X+1 to
+ * X+1+(X/32).
+ *
+ * At least one of such arguments is not zero - invocation without default
+ * arguments uses original non synthetic method.
+ *
+ * This filter marks IFEQ instructions as ignored.
*/
public final class KotlinDefaultArgumentsFilter implements IFilter {
@@ -132,19 +138,29 @@ public void match(final MethodNode methodNode,
private static int maskVar(final String desc,
final boolean constructor) {
+ final Type[] argumentTypes = Type.getMethodType(desc)
+ .getArgumentTypes();
int slot = 0;
if (constructor) {
// one slot for reference to current object
slot++;
}
- final Type[] argumentTypes = Type.getMethodType(desc)
- .getArgumentTypes();
- final int penultimateArgument = argumentTypes.length - 2;
- for (int i = 0; i < penultimateArgument; i++) {
+ final int firstMaskArgument = argumentTypes.length - 1
+ - computeNumberOfMaskArguments(argumentTypes.length);
+ for (int i = 0; i < firstMaskArgument; i++) {
slot += argumentTypes[i].getSize();
}
return slot;
}
}
+ /**
+ * @param arguments
+ * number of arguments of synthetic method
+ * @return number of arguments holding mask
+ */
+ static int computeNumberOfMaskArguments(final int arguments) {
+ return (arguments - 2) / 33 + 1;
+ }
+
}
diff --git a/org.jacoco.doc/docroot/doc/changes.html b/org.jacoco.doc/docroot/doc/changes.html
index 1d0b56df81..90b0340f97 100644
--- a/org.jacoco.doc/docroot/doc/changes.html
+++ b/org.jacoco.doc/docroot/doc/changes.html
@@ -26,6 +26,13 @@
Branches added by the Kotlin compiler for functions with default arguments and
+ having more than 32 parameters are filtered out during generation of report
+ (GitHub #1556).
+
+
Release 0.8.11 (2023/10/14)
New Features
From 59d4004065c27e09f795c6a18a3581f4bc51be18 Mon Sep 17 00:00:00 2001
From: Evgeny Mandrikov <138671+Godin@users.noreply.github.com>
Date: Wed, 17 Jan 2024 16:39:01 +0100
Subject: [PATCH 014/255] Add Skippy to the list of third-party integrations
(#1564)
---
org.jacoco.doc/docroot/doc/integrations.html | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/org.jacoco.doc/docroot/doc/integrations.html b/org.jacoco.doc/docroot/doc/integrations.html
index 7eb10f8fbe..08d2e7ea39 100644
--- a/org.jacoco.doc/docroot/doc/integrations.html
+++ b/org.jacoco.doc/docroot/doc/integrations.html
@@ -137,6 +137,10 @@
+ * runBlocking {
+ * // suspending lambda without suspension points,
+ * // i.e. without invocations of suspending functions/lambdas
+ * }
+ *
+ *
+ * https://github.com/JetBrains/kotlin/commit/f4a1e27124f77b2ffca576f7393218373c6ae085
+ *
+ * @see #should_filter_suspending_lambdas()
+ */
+ @Test
+ public void should_filter_Kotlin_1_6_suspending_lambda_without_suspension_points() {
+ final MethodNode m = new MethodNode(InstrSupport.ASM_API_VERSION,
+ Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL, "invokeSuspend",
+ "(Ljava/lang/Object;)Ljava/lang/Object;", null, null);
+ context.classAnnotations
+ .add(KotlinGeneratedFilter.KOTLIN_METADATA_DESC);
+
+ m.visitMethodInsn(Opcodes.INVOKESTATIC,
+ "kotlin/coroutines/intrinsics/IntrinsicsKt",
+ "getCOROUTINE_SUSPENDED", "()Ljava/lang/Object;", false);
+ final Range range1 = new Range();
+ range1.fromInclusive = m.instructions.getLast();
+ m.visitInsn(Opcodes.POP);
+
+ m.visitVarInsn(Opcodes.ALOAD, 0);
+ m.visitFieldInsn(Opcodes.GETFIELD, "ExampleKt$example$1", "label", "I");
+
+ final Label dflt = new Label();
+ final Label state0 = new Label();
+ m.visitTableSwitchInsn(0, 0, dflt, state0);
+
+ m.visitLabel(state0);
+ {
+ m.visitVarInsn(Opcodes.ALOAD, 1);
+ m.visitMethodInsn(Opcodes.INVOKESTATIC, "kotlin/ResultKt",
+ "throwOnFailure", "(Ljava/lang/Object;)V", false);
+ }
+ range1.toInclusive = m.instructions.getLast();
+
+ m.visitFieldInsn(Opcodes.GETSTATIC, "kotlin/Unit", "INSTANCE",
+ "Lkotlin/Unit;");
+ m.visitInsn(Opcodes.ARETURN);
+
+ m.visitLabel(dflt);
+ final Range range0 = new Range();
+ range0.fromInclusive = m.instructions.getLast();
+ m.visitTypeInsn(Opcodes.NEW, "java/lang/IllegalStateException");
+ m.visitInsn(Opcodes.DUP);
+ m.visitLdcInsn("call to 'resume' before 'invoke' with coroutine");
+ m.visitMethodInsn(Opcodes.INVOKESPECIAL,
+ "java/lang/IllegalStateException", "",
+ "(Ljava/lang/String;)V", false);
+ m.visitInsn(Opcodes.ATHROW);
+ range0.toInclusive = m.instructions.getLast();
+
+ filter.filter(m, context, output);
+
+ assertIgnored(range0, range1);
+ }
+
}
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinCoroutineFilter.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinCoroutineFilter.java
index 0fc360cb10..6dd47fd413 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinCoroutineFilter.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinCoroutineFilter.java
@@ -94,14 +94,23 @@ private void match(final MethodNode methodNode,
"getCOROUTINE_SUSPENDED", "()Ljava/lang/Object;");
}
- nextIsVar(Opcodes.ASTORE, "COROUTINE_SUSPENDED");
- nextIsVar(Opcodes.ALOAD, "this");
- nextIs(Opcodes.GETFIELD);
- nextIs(Opcodes.TABLESWITCH);
- if (cursor == null) {
- return;
+ final TableSwitchInsnNode s;
+ if (cursor != null
+ && Opcodes.POP == skipNonOpcodes(cursor.getNext())
+ .getOpcode()) {
+ // suspending lambda without suspension points
+ nextIs(Opcodes.POP);
+ s = nextIsStateSwitch();
+ if (s == null || s.labels.size() != 1) {
+ return;
+ }
+ } else {
+ nextIsVar(Opcodes.ASTORE, "COROUTINE_SUSPENDED");
+ s = nextIsStateSwitch();
+ if (s == null) {
+ return;
+ }
}
- final TableSwitchInsnNode s = (TableSwitchInsnNode) cursor;
final List ignore = new ArrayList(
s.labels.size() * 2);
@@ -174,6 +183,16 @@ private void match(final MethodNode methodNode,
}
}
+ private TableSwitchInsnNode nextIsStateSwitch() {
+ nextIsVar(Opcodes.ALOAD, "this");
+ nextIs(Opcodes.GETFIELD);
+ nextIs(Opcodes.TABLESWITCH);
+ if (cursor == null) {
+ return null;
+ }
+ return (TableSwitchInsnNode) cursor;
+ }
+
private void nextIsThrowOnFailure() {
final AbstractInsnNode c = cursor;
nextIsInvoke(Opcodes.INVOKESTATIC, "kotlin/ResultKt",
diff --git a/org.jacoco.doc/docroot/doc/changes.html b/org.jacoco.doc/docroot/doc/changes.html
index 7f8347b791..2dec020d62 100644
--- a/org.jacoco.doc/docroot/doc/changes.html
+++ b/org.jacoco.doc/docroot/doc/changes.html
@@ -30,6 +30,9 @@
New Features
Part of bytecode generated by the Kotlin compiler for inline value classes is
filtered out during generation of report
(GitHub #1475).
+
Part of bytecode generated by the Kotlin compiler for suspending lambdas
+ without suspension points is filtered out during generation of report
+ (GitHub #1283).
Method getEntries generated by the Kotlin compiler for enum
classes is filtered out during generation of report
(GitHub #1625).
From 5fdd9e6e4ac5db18b89433d13e7c34a144ef7a7c Mon Sep 17 00:00:00 2001
From: Evgeny Mandrikov <138671+Godin@users.noreply.github.com>
Date: Sun, 14 Jul 2024 18:52:04 +0200
Subject: [PATCH 063/255] Add builds with ECJ to GitHub Actions (#1649)
---
.github/workflows/ci.yml | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 60fddb7341..098418b581 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -9,10 +9,18 @@ jobs:
matrix:
include:
- jdk: 8
+ - jdk: 8
+ ecj: true
+ - jdk: 11
- jdk: 11
+ ecj: true
- jdk: 17
+ - jdk: 17
+ ecj: true
+ - jdk: 21
- jdk: 21
- name: JDK ${{ matrix.jdk }}
+ ecj: true
+ name: JDK ${{ matrix.jdk }}${{ matrix.ecj && ' with ECJ' || ''}}
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
@@ -45,6 +53,7 @@ jobs:
run: |
mvn -V -B -e --no-transfer-progress \
verify -Djdk.version=${{ matrix.jdk }} -Dbytecode.version=${{ matrix.jdk }} \
+ ${{ matrix.ecj && '-Decj' || ''}} \
--toolchains=toolchains.xml
Windows:
runs-on: windows-2022
From 4f851cae6822289c55aff539786405d2bf473584 Mon Sep 17 00:00:00 2001
From: Evgeny Mandrikov <138671+Godin@users.noreply.github.com>
Date: Sat, 20 Jul 2024 07:12:09 +0200
Subject: [PATCH 064/255] Reduce code duplication (#1652)
---
.../agent/rt/internal/ClassFileDumperTest.java | 10 +++-------
.../agent/rt/internal/CoverageTransformerTest.java | 11 +++--------
.../src/org/jacoco/core/test/TargetLoader.java | 12 ++++--------
3 files changed, 10 insertions(+), 23 deletions(-)
diff --git a/org.jacoco.agent.rt.test/src/org/jacoco/agent/rt/internal/ClassFileDumperTest.java b/org.jacoco.agent.rt.test/src/org/jacoco/agent/rt/internal/ClassFileDumperTest.java
index c493d3416e..f8cae15ec2 100644
--- a/org.jacoco.agent.rt.test/src/org/jacoco/agent/rt/internal/ClassFileDumperTest.java
+++ b/org.jacoco.agent.rt.test/src/org/jacoco/agent/rt/internal/ClassFileDumperTest.java
@@ -14,12 +14,12 @@
import static org.junit.Assert.assertArrayEquals;
-import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
+import org.jacoco.core.internal.InputStreams;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -66,13 +66,9 @@ public void testNoDumps() throws IOException {
private void assertContents(File location, String filename)
throws IOException {
InputStream in = new FileInputStream(new File(location, filename));
- ByteArrayOutputStream buffer = new ByteArrayOutputStream();
- int b;
- while ((b = in.read()) != -1) {
- buffer.write(b);
- }
+ final byte[] bytes = InputStreams.readFully(in);
in.close();
- assertArrayEquals(contents, buffer.toByteArray());
+ assertArrayEquals(contents, bytes);
}
}
diff --git a/org.jacoco.agent.rt.test/src/org/jacoco/agent/rt/internal/CoverageTransformerTest.java b/org.jacoco.agent.rt.test/src/org/jacoco/agent/rt/internal/CoverageTransformerTest.java
index d53e190570..63eba1a6f0 100644
--- a/org.jacoco.agent.rt.test/src/org/jacoco/agent/rt/internal/CoverageTransformerTest.java
+++ b/org.jacoco.agent.rt.test/src/org/jacoco/agent/rt/internal/CoverageTransformerTest.java
@@ -18,7 +18,6 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.instrument.IllegalClassFormatException;
@@ -27,6 +26,7 @@
import java.security.cert.Certificate;
import org.jacoco.core.JaCoCo;
+import org.jacoco.core.internal.InputStreams;
import org.jacoco.core.runtime.AbstractRuntime;
import org.jacoco.core.runtime.AgentOptions;
import org.junit.After;
@@ -245,14 +245,9 @@ private static byte[] getClassData(Class> clazz) throws IOException {
final String resource = "/" + clazz.getName().replace('.', '/')
+ ".class";
final InputStream in = clazz.getResourceAsStream(resource);
- final ByteArrayOutputStream out = new ByteArrayOutputStream();
- byte[] buffer = new byte[0x100];
- int len;
- while ((len = in.read(buffer)) != -1) {
- out.write(buffer, 0, len);
- }
+ final byte[] bytes = InputStreams.readFully(in);
in.close();
- return out.toByteArray();
+ return bytes;
}
private static class StubRuntime extends AbstractRuntime {
diff --git a/org.jacoco.core.test/src/org/jacoco/core/test/TargetLoader.java b/org.jacoco.core.test/src/org/jacoco/core/test/TargetLoader.java
index 431516b5a9..84daacb3e8 100644
--- a/org.jacoco.core.test/src/org/jacoco/core/test/TargetLoader.java
+++ b/org.jacoco.core.test/src/org/jacoco/core/test/TargetLoader.java
@@ -12,12 +12,13 @@
*******************************************************************************/
package org.jacoco.core.test;
-import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
+import org.jacoco.core.internal.InputStreams;
+
/**
* Loads given classes from a byte arrays.
*/
@@ -68,14 +69,9 @@ public static byte[] getClassDataAsBytes(Class> clazz)
}
private static byte[] readBytes(InputStream in) throws IOException {
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- byte[] buffer = new byte[0x100];
- int len;
- while ((len = in.read(buffer)) != -1) {
- out.write(buffer, 0, len);
- }
+ final byte[] bytes = InputStreams.readFully(in);
in.close();
- return out.toByteArray();
+ return bytes;
}
@Override
From eda47f1a223d387accedeaed7a3d4f9096fbe5ba Mon Sep 17 00:00:00 2001
From: Evgeny Mandrikov <138671+Godin@users.noreply.github.com>
Date: Mon, 29 Jul 2024 22:13:36 +0300
Subject: [PATCH 065/255] Remove unused field (#1657)
---
.../internal/analysis/filter/KotlinLateinitFilterTest.java | 4 ----
1 file changed, 4 deletions(-)
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
index c9b8dc87d7..5672d35260 100644
--- 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
@@ -12,7 +12,6 @@
*******************************************************************************/
package org.jacoco.core.internal.analysis.filter;
-import org.jacoco.core.internal.instr.InstrSupport;
import org.junit.Test;
import org.objectweb.asm.Label;
import org.objectweb.asm.Opcodes;
@@ -26,9 +25,6 @@ public class KotlinLateinitFilterTest extends FilterTestBase {
private final KotlinLateinitFilter filter = new KotlinLateinitFilter();
- private final MethodNode m = new MethodNode(InstrSupport.ASM_API_VERSION, 0,
- "name", "()V", null, null);
-
@Test
public void should_filter_Kotlin_1_2() {
final MethodNode m = new MethodNode(0, "read", "()Ljava/lang/String;",
From 7222848bef6a43a15a0809596f024544ecf38397 Mon Sep 17 00:00:00 2001
From: Evgeny Mandrikov <138671+Godin@users.noreply.github.com>
Date: Mon, 29 Jul 2024 22:40:56 +0300
Subject: [PATCH 066/255] Add missing `Test` annotation to method in
`FileOutputTest` (#1658)
This was overlooked in aa16a7c25cfc119ba3486bb114d3d54dc1bcca6c
---
.../src/org/jacoco/agent/rt/internal/output/FileOutputTest.java | 1 +
1 file changed, 1 insertion(+)
diff --git a/org.jacoco.agent.rt.test/src/org/jacoco/agent/rt/internal/output/FileOutputTest.java b/org.jacoco.agent.rt.test/src/org/jacoco/agent/rt/internal/output/FileOutputTest.java
index ccfc2b2375..9422020e42 100644
--- a/org.jacoco.agent.rt.test/src/org/jacoco/agent/rt/internal/output/FileOutputTest.java
+++ b/org.jacoco.agent.rt.test/src/org/jacoco/agent/rt/internal/output/FileOutputTest.java
@@ -108,6 +108,7 @@ public void startup_should_throw_OverlappingFileLockException_when_execfile_is_p
}
}
+ @Test
public void startup_should_throw_InterruptedIOException_when_execfile_is_locked_and_thread_is_interrupted()
throws Exception {
if (JavaVersion.current().isBefore("1.6")) {
From ad12d46652db64975a2fb6718a941c3ada2ea6be Mon Sep 17 00:00:00 2001
From: Evgeny Mandrikov <138671+Godin@users.noreply.github.com>
Date: Tue, 30 Jul 2024 15:55:32 +0200
Subject: [PATCH 067/255] Fix typo: replace two consecutive dots in javadoc by
one (#1659)
---
.../jacoco/report/internal/html/page/PackageSourcePage.java | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/org.jacoco.report/src/org/jacoco/report/internal/html/page/PackageSourcePage.java b/org.jacoco.report/src/org/jacoco/report/internal/html/page/PackageSourcePage.java
index fc1f24b4f0..7e4b0cc48d 100644
--- a/org.jacoco.report/src/org/jacoco/report/internal/html/page/PackageSourcePage.java
+++ b/org.jacoco.report/src/org/jacoco/report/internal/html/page/PackageSourcePage.java
@@ -70,8 +70,7 @@ public void render() throws IOException {
/**
* Returns the link to the source file page of the source file with the
- * given name. If no source file was located, null is
- * returned..
+ * given name. If no source file was located, null is returned.
*/
ILinkable getSourceFilePage(final String name) {
return sourceFilePages.get(name);
From 1ba265ee1cd5bc354ee11ac1ca7a2ce8e71d3a98 Mon Sep 17 00:00:00 2001
From: Evgeny Mandrikov <138671+Godin@users.noreply.github.com>
Date: Wed, 31 Jul 2024 00:11:48 +0200
Subject: [PATCH 068/255] Fix interpretation of Kotlin SMAP (#1525)
---
.../kotlin/KotlinCrossinlineTest.java | 32 ++++
.../kotlin/targets/KotlinCrossinlineTarget.kt | 37 +++++
.../filter/KotlinInlineFilterTest.java | 150 +++++++++++++++---
.../analysis/filter/KotlinInlineFilter.java | 18 +--
org.jacoco.doc/docroot/doc/changes.html | 6 +
5 files changed, 209 insertions(+), 34 deletions(-)
create mode 100644 org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinCrossinlineTest.java
create mode 100644 org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinCrossinlineTarget.kt
diff --git a/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinCrossinlineTest.java b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinCrossinlineTest.java
new file mode 100644
index 0000000000..ce1f317372
--- /dev/null
+++ b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinCrossinlineTest.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * 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.KotlinCrossinlineTarget;
+
+/**
+ * Test of code coverage in {@link KotlinCrossinlineTarget}.
+ */
+public class KotlinCrossinlineTest extends ValidationTestBase {
+
+ public KotlinCrossinlineTest() {
+ super(KotlinCrossinlineTarget.class);
+ }
+
+ @Override
+ public void all_missed_instructions_should_have_line_number() {
+ // missed instructions without line number in inline function
+ }
+
+}
diff --git a/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinCrossinlineTarget.kt b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinCrossinlineTarget.kt
new file mode 100644
index 0000000000..f0a58015de
--- /dev/null
+++ b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinCrossinlineTarget.kt
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * 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 `crossinline`.
+ */
+object KotlinCrossinlineTarget {
+
+ inline fun example(crossinline lambda: () -> Unit): () -> Unit { // assertEmpty()
+ return { // assertNotCovered()
+ lambda() // assertEmpty()
+ } // assertEmpty()
+ } // assertEmpty()
+
+ @JvmStatic
+ fun main(args: Array) {
+
+ example { // assertFullyCovered()
+ nop() // assertEmpty()
+ }() // assertEmpty()
+
+ }
+
+}
diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinInlineFilterTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinInlineFilterTest.java
index 2e7d1622c8..73b77a097e 100644
--- a/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinInlineFilterTest.java
+++ b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinInlineFilterTest.java
@@ -22,6 +22,7 @@
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;
/**
@@ -36,6 +37,7 @@ public class KotlinInlineFilterTest extends FilterTestBase {
@Test
public void should_filter() {
+ context.className = "CallsiteKt";
context.sourceFileName = "callsite.kt";
context.sourceDebugExtension = "" //
+ "SMAP\n" //
@@ -113,6 +115,7 @@ public void should_filter() {
*/
@Test
public void should_filter_when_in_same_file() {
+ context.className = "Callsite";
context.sourceFileName = "example.kt";
context.sourceDebugExtension = "" //
+ "SMAP\n" //
@@ -173,6 +176,7 @@ public void should_filter_when_in_same_file() {
*/
@Test
public void should_filter_without_parsing_KotlinDebug_stratum() {
+ context.className = "ExampleKt";
context.sourceFileName = "Example.kt";
context.sourceDebugExtension = "" //
+ "SMAP\n" //
@@ -204,6 +208,127 @@ public void should_filter_without_parsing_KotlinDebug_stratum() {
assertIgnored(expectedRanges.toArray(new Range[0]));
}
+ /**
+ *
+ * package a;
+ *
+ * inline fun testInline() {} // line 7
+ *
+ */
+ @Test
+ public void should_stop_parsing_at_KotlinDebug_stratum() {
+ final KotlinSMAP smap = new KotlinSMAP("Example.kt", "SMAP\n" //
+ + "Example.kt\n" // OutputFileName=Example.kt
+ + "Kotlin\n" // DefaultStratumId=Kotlin
+ + "*S Kotlin\n" // StratumID=Kotlin
+ + "*F\n" // FileSection
+ + "+ 1 Example.kt\n" // FileID=1,FileName=Example.kt
+ + "ExampleKt\n" //
+ + "*L\n" // LineSection
+ + "1#1,3:1\n" // InputStartLine=1,LineFileID=1,RepeatCount=3,OutputStartLine=1
+ + "1#1:4\n" // InputStartLine=1,LineFileID=1,OutputStartLine=4
+ + "*S KotlinDebug\n" // StratumID=KotlinDebug
+ + "xxx");
+ assertEquals(2, smap.mappings().size());
+ KotlinSMAP.Mapping mapping = smap.mappings().get(0);
+ assertEquals("ExampleKt", mapping.inputClassName());
+ assertEquals(1, mapping.inputStartLine());
+ assertEquals(3, mapping.repeatCount());
+ assertEquals(1, mapping.outputStartLine());
+ mapping = smap.mappings().get(1);
+ assertEquals("ExampleKt", mapping.inputClassName());
+ assertEquals(1, mapping.inputStartLine());
+ assertEquals(1, mapping.repeatCount());
+ assertEquals(4, mapping.outputStartLine());
+ }
+
+ @Test
+ public void should_throw_exception_when_not_an_SMAP_Header() {
+ try {
+ new KotlinSMAP("", "xxx");
+ fail("exception expected");
+ } catch (final IllegalStateException e) {
+ assertEquals("Unexpected SMAP line: xxx", e.getMessage());
+ }
+ }
+
+ @Test
+ public void should_throw_exception_when_OutputFileName_does_not_match_SourceFileName() {
+ try {
+ new KotlinSMAP("", "SMAP\n" //
+ + "Example.kt\n");
+ fail("exception expected");
+ } catch (final IllegalStateException e) {
+ assertEquals("Unexpected SMAP line: Example.kt", e.getMessage());
+ }
+ }
+
+ @Test
+ public void should_throw_exception_when_DefaultStratumId_is_not_Kotlin() {
+ try {
+ new KotlinSMAP("Servlet.java", "SMAP\n" //
+ + "Servlet.java\n" // OutputFileName=Servlet.java
+ + "JSP\n"); // DefaultStratumId=JSP
+ fail("exception expected");
+ } catch (final IllegalStateException e) {
+ assertEquals("Unexpected SMAP line: JSP", e.getMessage());
+ }
+ }
+
+ @Test
+ public void should_throw_exception_when_first_StratumId_is_not_Kotlin() {
+ try {
+ new KotlinSMAP("Example.kt", "SMAP\n" //
+ + "Example.kt\n" // OutputFileName=Example.kt
+ + "Kotlin\n" // DefaultStratumId=Kotlin
+ + "*S KotlinDebug\n"); // StratumID=KotlinDebug
+ fail("exception expected");
+ } catch (final IllegalStateException e) {
+ assertEquals("Unexpected SMAP line: *S KotlinDebug",
+ e.getMessage());
+ }
+ }
+
+ @Test
+ public void should_throw_exception_when_StratumSection_does_not_start_with_FileSection() {
+ try {
+ new KotlinSMAP("Example.kt", "SMAP\n" //
+ + "Example.kt\n" //
+ + "Kotlin\n" //
+ + "*S Kotlin\n" //
+ + "xxx"); //
+ fail("exception expected");
+ } catch (final IllegalStateException e) {
+ assertEquals("Unexpected SMAP line: xxx", e.getMessage());
+ }
+ }
+
+ @Test
+ public void should_throw_exception_when_FileSection_contains_unexpected_FileInfo() {
+ try {
+ new KotlinSMAP("Example.kt", "SMAP\n" //
+ + "Example.kt\n" //
+ + "Kotlin\n" //
+ + "*S Kotlin\n" //
+ + "*F\n" //
+ + "xxx"); //
+ fail("exception expected");
+ } catch (final IllegalStateException e) {
+ assertEquals("Unexpected SMAP line: xxx", e.getMessage());
+ }
+ }
+
+ @Test
+ public void should_throw_exception_when_LineSection_contains_unexpected_LineInfo() {
+ try {
+ new KotlinSMAP("Example.kt", "SMAP\n" //
+ + "Example.kt\n" //
+ + "Kotlin\n" //
+ + "*S Kotlin\n" //
+ + "*F\n" //
+ + "*L\n" //
+ + "xxx"); //
+ fail("exception expected");
+ } catch (final IllegalStateException e) {
+ assertEquals("Unexpected SMAP line: xxx", e.getMessage());
+ }
+ }
+
+ @Test
+ public void should_throw_exception_when_LineInfo_does_not_have_FileID() {
+ try {
+ new KotlinSMAP("Example.kt", "SMAP\n" //
+ + "Example.kt\n" //
+ + "Kotlin\n" //
+ + "*S Kotlin\n" //
+ + "*F\n" //
+ + "*L\n" //
+ + "1:1\n"); // InputStartLine=1,OutputStartLine=1
+ fail("exception expected");
+ } catch (final NullPointerException e) {
+ // expected
+ }
+ }
+
+}
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinInlineFilter.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinInlineFilter.java
index 43e328e977..55c2d77fbf 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinInlineFilter.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinInlineFilter.java
@@ -12,13 +12,6 @@
*******************************************************************************/
package org.jacoco.core.internal.analysis.filter;
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.StringReader;
-import java.util.BitSet;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.LineNumberNode;
import org.objectweb.asm.tree.MethodNode;
@@ -57,85 +50,18 @@ public void filter(final MethodNode methodNode,
}
}
- private static int getFirstGeneratedLineNumber(
- final String currentClassName, final String sourceFileName,
- final String smap) {
- try {
- final BufferedReader br = new BufferedReader(
- new StringReader(smap));
- expectLine(br, "SMAP");
- // OutputFileName
- expectLine(br, sourceFileName);
- // DefaultStratumId
- expectLine(br, "Kotlin");
- // StratumSection
- expectLine(br, "*S Kotlin");
- // FileSection
- expectLine(br, "*F");
- final BitSet sourceFileIds = new BitSet();
- String line;
- while (!"*L".equals(line = br.readLine())) {
- final Matcher m = FILE_INFO_PATTERN.matcher(line);
- if (!m.matches()) {
- throw new IllegalStateException(
- "Unexpected SMAP line: " + line);
- }
- // See
- // https://github.com/JetBrains/kotlin/blob/2.0.0/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/SMAP.kt#L120-L121
- // https://github.com/JetBrains/kotlin/blob/2.0.0/compiler/backend/src/org/jetbrains/kotlin/codegen/SourceInfo.kt#L38-L41
- final String className = br.readLine();
- if (currentClassName.equals(className)) {
- sourceFileIds.set(Integer.parseInt(m.group(1)));
- }
- }
- // LineSection
- int min = Integer.MAX_VALUE;
- while (true) {
- line = br.readLine();
- if (line.equals("*E") || line.equals("*S KotlinDebug")) {
- break;
- }
- final Matcher m = LINE_INFO_PATTERN.matcher(line);
- if (!m.matches()) {
- throw new IllegalStateException(
- "Unexpected SMAP line: " + line);
- }
- final int inputStartLine = Integer.parseInt(m.group(1));
- final int lineFileID = Integer
- .parseInt(m.group(2).substring(1));
- final int outputStartLine = Integer.parseInt(m.group(4));
- if (sourceFileIds.get(lineFileID)
- && inputStartLine == outputStartLine) {
- continue;
- }
- min = Math.min(outputStartLine, min);
+ private static int getFirstGeneratedLineNumber(final String className,
+ final String sourceFileName, final String smap) {
+ int min = Integer.MAX_VALUE;
+ for (KotlinSMAP.Mapping mapping : new KotlinSMAP(sourceFileName, smap)
+ .mappings()) {
+ if (className.equals(mapping.inputClassName())
+ && mapping.inputStartLine() == mapping.outputStartLine()) {
+ continue;
}
- return min;
- } catch (final IOException e) {
- // Must not happen with StringReader
- throw new AssertionError(e);
+ min = Math.min(mapping.outputStartLine(), min);
}
+ return min;
}
- private static void expectLine(final BufferedReader br,
- final String expected) throws IOException {
- final String line = br.readLine();
- if (!expected.equals(line)) {
- throw new IllegalStateException("Unexpected SMAP line: " + line);
- }
- }
-
- private static final Pattern LINE_INFO_PATTERN = Pattern.compile("" //
- + "([0-9]++)" // InputStartLine
- + "(#[0-9]++)?+" // LineFileID
- + "(,[0-9]++)?+" // RepeatCount
- + ":([0-9]++)" // OutputStartLine
- + "(,[0-9]++)?+" // OutputLineIncrement
- );
-
- private static final Pattern FILE_INFO_PATTERN = Pattern.compile("" //
- + "\\+ ([0-9]++)" // FileID
- + " (.++)" // FileName
- );
-
}
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinSMAP.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinSMAP.java
new file mode 100644
index 0000000000..8062199ae4
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinSMAP.java
@@ -0,0 +1,182 @@
+/*******************************************************************************
+ * 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 java.io.BufferedReader;
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Parsed representation of SourceDebugExtension attribute.
+ */
+final class KotlinSMAP {
+
+ public static final class Mapping {
+ private final String inputClassName;
+ private final int inputStartLine;
+ private final int repeatCount;
+ private final int outputStartLine;
+
+ /**
+ * Creates a new mapping.
+ *
+ * @param inputClassName
+ * name of input class
+ * @param inputStartLine
+ * starting line in input
+ * @param repeatCount
+ * number of mapped lines
+ * @param outputStartLine
+ * starting line in output
+ */
+ Mapping(final String inputClassName, final int inputStartLine,
+ final int repeatCount, final int outputStartLine) {
+ this.inputClassName = inputClassName;
+ this.inputStartLine = inputStartLine;
+ this.repeatCount = repeatCount;
+ this.outputStartLine = outputStartLine;
+ }
+
+ /**
+ * @return name of input class
+ */
+ public String inputClassName() {
+ return inputClassName;
+ }
+
+ /**
+ * @return starting line in input
+ */
+ public int inputStartLine() {
+ return inputStartLine;
+ }
+
+ /**
+ * @return number of mapped lines
+ */
+ public int repeatCount() {
+ return repeatCount;
+ }
+
+ /**
+ * @return starting line in output
+ */
+ public int outputStartLine() {
+ return outputStartLine;
+ }
+ }
+
+ private final ArrayList mappings = new ArrayList();
+
+ /**
+ * Returns list of mappings.
+ *
+ * @return list of mappings
+ */
+ public List mappings() {
+ return mappings;
+ }
+
+ /**
+ * Creates parsed representation of provided SourceDebugExtension attribute.
+ *
+ * @param sourceFileName
+ * the name of the source file from which the class with SMAP was
+ * compiled
+ * @param smap
+ * value of SourceDebugExtension attribute to parse
+ */
+ public KotlinSMAP(final String sourceFileName, final String smap) {
+ try {
+ final BufferedReader br = new BufferedReader(
+ new StringReader(smap));
+ // Header
+ expectLine(br, "SMAP");
+ // OutputFileName
+ expectLine(br, sourceFileName);
+ // DefaultStratumId
+ expectLine(br, "Kotlin");
+ // StratumSection
+ expectLine(br, "*S Kotlin");
+ // FileSection
+ expectLine(br, "*F");
+ final HashMap inputClassNames = new HashMap();
+ String line;
+ while (!"*L".equals(line = br.readLine())) {
+ final Matcher m = FILE_INFO_PATTERN.matcher(line);
+ if (!m.matches()) {
+ throw new IllegalStateException(
+ "Unexpected SMAP line: " + line);
+ }
+ final int id = Integer.parseInt(m.group(1));
+ // See
+ // https://github.com/JetBrains/kotlin/blob/2.0.0/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/SMAP.kt#L120-L121
+ // https://github.com/JetBrains/kotlin/blob/2.0.0/compiler/backend/src/org/jetbrains/kotlin/codegen/SourceInfo.kt#L38-L41
+ final String className = br.readLine();
+ inputClassNames.put(id, className);
+ }
+ // LineSection
+ while (true) {
+ line = br.readLine();
+ if (line.equals("*E") || line.equals("*S KotlinDebug")) {
+ break;
+ }
+ final Matcher m = LINE_INFO_PATTERN.matcher(line);
+ if (!m.matches()) {
+ throw new IllegalStateException(
+ "Unexpected SMAP line: " + line);
+ }
+ final int inputStartLine = Integer.parseInt(m.group(1));
+ final int lineFileID = Integer
+ .parseInt(m.group(2).substring(1));
+ final String repeatCountOptional = m.group(3);
+ final int repeatCount = repeatCountOptional != null
+ ? Integer.parseInt(repeatCountOptional.substring(1))
+ : 1;
+ final int outputStartLine = Integer.parseInt(m.group(4));
+ mappings.add(new Mapping(inputClassNames.get(lineFileID),
+ inputStartLine, repeatCount, outputStartLine));
+ }
+ } catch (final IOException e) {
+ // Must not happen with StringReader
+ throw new AssertionError(e);
+ }
+ }
+
+ private static void expectLine(final BufferedReader br,
+ final String expected) throws IOException {
+ final String line = br.readLine();
+ if (!expected.equals(line)) {
+ throw new IllegalStateException("Unexpected SMAP line: " + line);
+ }
+ }
+
+ private static final Pattern LINE_INFO_PATTERN = Pattern.compile("" //
+ + "([0-9]++)" // InputStartLine
+ + "(#[0-9]++)?+" // LineFileID
+ + "(,[0-9]++)?+" // RepeatCount
+ + ":([0-9]++)" // OutputStartLine
+ + "(,[0-9]++)?+" // OutputLineIncrement
+ );
+
+ private static final Pattern FILE_INFO_PATTERN = Pattern.compile("" //
+ + "\\+ ([0-9]++)" // FileID
+ + " (.++)" // FileName
+ );
+
+}
From 3935957ba891cca5e0a88d8639becad05689154c Mon Sep 17 00:00:00 2001
From: Evgeny Mandrikov <138671+Godin@users.noreply.github.com>
Date: Sun, 4 Aug 2024 22:05:04 +0200
Subject: [PATCH 071/255] Remove unused import (#1666)
---
.../jacoco/core/test/validation/kotlin/KotlinSafeCastTest.java | 1 -
1 file changed, 1 deletion(-)
diff --git a/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinSafeCastTest.java b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinSafeCastTest.java
index 6eb1d5760d..95745918a5 100644
--- a/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinSafeCastTest.java
+++ b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinSafeCastTest.java
@@ -13,7 +13,6 @@
package org.jacoco.core.test.validation.kotlin;
import org.jacoco.core.test.validation.ValidationTestBase;
-import org.jacoco.core.test.validation.kotlin.targets.KotlinElvisOperatorTarget;
import org.jacoco.core.test.validation.kotlin.targets.KotlinSafeCastTarget;
/**
From c2d805a8c73615c35d0eea5a7887f7eff412fef4 Mon Sep 17 00:00:00 2001
From: Evgeny Mandrikov <138671+Godin@users.noreply.github.com>
Date: Sun, 4 Aug 2024 22:50:25 +0200
Subject: [PATCH 072/255] Add missing test (#1667)
It was forgotten in commit 281538bc74d9f09f5b0d83250a4435b5c2732e21
---
.../jacoco/core/internal/analysis/ClassAnalyzerTest.java | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/ClassAnalyzerTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/ClassAnalyzerTest.java
index 84bb3ba21a..ee01b9ac04 100644
--- a/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/ClassAnalyzerTest.java
+++ b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/ClassAnalyzerTest.java
@@ -60,6 +60,13 @@ public void testMethodFilter_Empty() {
assertEquals(0, coverage.getMethods().size());
}
+ @Test
+ public void should_collect_annotations() {
+ assertTrue(analyzer.getClassAnnotations().isEmpty());
+ analyzer.visitAnnotation("Lpkg/Annotation;", true);
+ assertTrue(analyzer.getClassAnnotations().contains("Lpkg/Annotation;"));
+ }
+
@Test
public void should_collect_attributes() {
assertTrue(analyzer.getClassAttributes().isEmpty());
From 6492b4035fcd44c9fd4553d30b8576c2cb42c278 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 6 Aug 2024 09:59:30 +0200
Subject: [PATCH 073/255] Bump actions/upload-artifact from 4.3.4 to 4.3.5
(#1673)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.3.4 to 4.3.5.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/0b2256b8c012f0828dc542b3febcab082c67f72b...89ef406dd8d7e03cfd12d9e0a4a378f454709029)
---
updated-dependencies:
- dependency-name: actions/upload-artifact
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
.github/workflows/ci.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 098418b581..7f8a681f15 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -91,7 +91,7 @@ jobs:
mvn -V -B -e --no-transfer-progress \
verify -Djdk.version=6 -Dbytecode.version=5 \
--toolchains=toolchains.xml
- - uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4
+ - uses: actions/upload-artifact@89ef406dd8d7e03cfd12d9e0a4a378f454709029 # v4.3.5
id: artifact-upload-step
with:
name: jacoco
From 2940177428cdad9d669cec32dc3459fd857438a6 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 6 Aug 2024 11:46:54 +0200
Subject: [PATCH 074/255] Bump actions/setup-java from 4.2.1 to 4.2.2 (#1672)
Bumps [actions/setup-java](https://github.com/actions/setup-java) from 4.2.1 to 4.2.2.
- [Release notes](https://github.com/actions/setup-java/releases)
- [Commits](https://github.com/actions/setup-java/compare/99b8673ff64fbf99d8d325f52d9a5bdedb8483e9...6a0805fcefea3d4657a47ac4c165951e33482018)
---
updated-dependencies:
- dependency-name: actions/setup-java
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
.github/workflows/ci.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 7f8a681f15..67b3a08b9b 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -24,7 +24,7 @@ jobs:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- - uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4.2.1
+ - uses: actions/setup-java@6a0805fcefea3d4657a47ac4c165951e33482018 # v4.2.2
with:
distribution: 'zulu'
java-version: |
@@ -59,7 +59,7 @@ jobs:
runs-on: windows-2022
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- - uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4.2.1
+ - uses: actions/setup-java@6a0805fcefea3d4657a47ac4c165951e33482018 # v4.2.2
with:
distribution: 'zulu'
java-version: |
From e4ab395b8c37e19dc73b4396a259281384122297 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 8 Aug 2024 13:44:54 +0200
Subject: [PATCH 075/255] Upgrade Kotlin to 2.0.10 (#1674)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Evgeny Mandrikov
---
org.jacoco.core.test.validation.kotlin/pom.xml | 2 +-
org.jacoco.core.test.validation/pom.xml | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/org.jacoco.core.test.validation.kotlin/pom.xml b/org.jacoco.core.test.validation.kotlin/pom.xml
index b3984fc546..a420a3e6fb 100644
--- a/org.jacoco.core.test.validation.kotlin/pom.xml
+++ b/org.jacoco.core.test.validation.kotlin/pom.xml
@@ -25,7 +25,7 @@
JaCoCo :: Test :: Core :: Validation Kotlin
- 2.0.0
+ 2.0.10
diff --git a/org.jacoco.core.test.validation/pom.xml b/org.jacoco.core.test.validation/pom.xml
index 0dae8d4d8c..6060229c79 100644
--- a/org.jacoco.core.test.validation/pom.xml
+++ b/org.jacoco.core.test.validation/pom.xml
@@ -504,7 +504,7 @@
-
+
2216
@@ -535,7 +535,7 @@
-
+
2216
From a4f2b16c23b1bfba0419edc1697cb13775c043a9 Mon Sep 17 00:00:00 2001
From: Evgeny Mandrikov <138671+Godin@users.noreply.github.com>
Date: Mon, 12 Aug 2024 08:08:18 +0200
Subject: [PATCH 076/255] Add missing javadoc (#1677)
It was forgotten in commit 36b4e9c7103441d556a1667b4485960f5bdfeff8
---
.../core/internal/instr/CondyProbeArrayStrategyTest.java | 3 +++
1 file changed, 3 insertions(+)
diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/instr/CondyProbeArrayStrategyTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/instr/CondyProbeArrayStrategyTest.java
index 13bca2efc6..e618e245bd 100644
--- a/org.jacoco.core.test/src/org/jacoco/core/internal/instr/CondyProbeArrayStrategyTest.java
+++ b/org.jacoco.core.test/src/org/jacoco/core/internal/instr/CondyProbeArrayStrategyTest.java
@@ -27,6 +27,9 @@
import org.objectweb.asm.tree.TypeInsnNode;
import org.objectweb.asm.tree.VarInsnNode;
+/**
+ * Unit test for {@link CondyProbeArrayStrategy}.
+ */
public class CondyProbeArrayStrategyTest {
private CondyProbeArrayStrategy strategy;
From a06b277e824d2b1667fab85e03cb58153c474741 Mon Sep 17 00:00:00 2001
From: Evgeny Mandrikov <138671+Godin@users.noreply.github.com>
Date: Mon, 12 Aug 2024 08:44:37 +0200
Subject: [PATCH 077/255] Upgrade Groovy to 3.0.22 (#1678)
---
org.jacoco.core.test.validation.groovy/pom.xml | 2 +-
org.jacoco.core.test.validation/pom.xml | 18 ++++++++----------
2 files changed, 9 insertions(+), 11 deletions(-)
diff --git a/org.jacoco.core.test.validation.groovy/pom.xml b/org.jacoco.core.test.validation.groovy/pom.xml
index a06c0d3518..0db9822cae 100644
--- a/org.jacoco.core.test.validation.groovy/pom.xml
+++ b/org.jacoco.core.test.validation.groovy/pom.xml
@@ -27,7 +27,7 @@
3.0.0
- 3.0.21
+ 3.0.22
diff --git a/org.jacoco.core.test.validation/pom.xml b/org.jacoco.core.test.validation/pom.xml
index 6060229c79..c9b4001d9a 100644
--- a/org.jacoco.core.test.validation/pom.xml
+++ b/org.jacoco.core.test.validation/pom.xml
@@ -368,7 +368,7 @@
18
-
+
1618
@@ -395,7 +395,7 @@
19
-
+
1619
@@ -422,7 +422,7 @@
20
-
+
1620
@@ -449,7 +449,7 @@
21
-
+
1621
@@ -477,7 +477,7 @@
22
-
+
1622
@@ -506,7 +506,7 @@
22
-
+
1623
@@ -519,9 +519,7 @@
../org.jacoco.core.test.validation.java14../org.jacoco.core.test.validation.java16../org.jacoco.core.test.validation.java21
-
../org.jacoco.core.test.validation.scala
@@ -537,7 +535,7 @@
22
-
+
1624
@@ -550,7 +548,7 @@
../org.jacoco.core.test.validation.java14../org.jacoco.core.test.validation.java16../org.jacoco.core.test.validation.java21
-
../org.jacoco.core.test.validation.scala
From 52e6e50d8490fca597092127d519947f48dca733 Mon Sep 17 00:00:00 2001
From: Evgeny Mandrikov <138671+Godin@users.noreply.github.com>
Date: Mon, 12 Aug 2024 09:10:15 +0200
Subject: [PATCH 078/255] Require at least Maven 3.6.3 for build (#1681)
---
org.jacoco.build/pom.xml | 2 +-
org.jacoco.doc/docroot/doc/build.html | 2 +-
org.jacoco.doc/docroot/doc/changes.html | 6 ++++++
org.jacoco.doc/docroot/doc/environment.html | 2 +-
4 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/org.jacoco.build/pom.xml b/org.jacoco.build/pom.xml
index 8476939e33..3e07eeb632 100644
--- a/org.jacoco.build/pom.xml
+++ b/org.jacoco.build/pom.xml
@@ -520,7 +520,7 @@
- [3.5.4,3.8.2),(3.8.2,)
+ [3.6.3,3.8.2),(3.8.2,)The rules for repo1.maven.org are that pom.xml files should not include repository definitions.
diff --git a/org.jacoco.doc/docroot/doc/build.html b/org.jacoco.doc/docroot/doc/build.html
index 89ed086914..efab7c1556 100644
--- a/org.jacoco.doc/docroot/doc/build.html
+++ b/org.jacoco.doc/docroot/doc/build.html
@@ -24,7 +24,7 @@
Build
The JaCoCo build is based on Maven and
can be locally executed on every machine with a proper
environment setup. In particular you need at
- least Maven 3.5.4 and JDK 17
+ least Maven 3.6.3 and JDK 17
installations. Developers are encouraged to run the build before every commit
to ensure consistency of the source tree.
diff --git a/org.jacoco.doc/docroot/doc/changes.html b/org.jacoco.doc/docroot/doc/changes.html
index 2dd5dcb1b5..4bddc1488a 100644
--- a/org.jacoco.doc/docroot/doc/changes.html
+++ b/org.jacoco.doc/docroot/doc/changes.html
@@ -44,6 +44,12 @@
The JaCoCo build is based on Maven
- and requires at least Maven 3.5.4 and JDK 17.
+ and requires at least Maven 3.6.3 and JDK 17.
See the build description for details.
From 137173919422f5e7fe81ace22bc9f0ae47e8cd54 Mon Sep 17 00:00:00 2001
From: Evgeny Mandrikov <138671+Godin@users.noreply.github.com>
Date: Mon, 12 Aug 2024 11:53:49 +0200
Subject: [PATCH 079/255] Fix ClassAnalyzerTest (#1668)
This was overlooked in commit 4d4d02e6dbfb17f454bf4579ee13209e9f035154
---
.../internal/analysis/ClassAnalyzerTest.java | 34 +++++++++++++++----
1 file changed, 28 insertions(+), 6 deletions(-)
diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/ClassAnalyzerTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/ClassAnalyzerTest.java
index ee01b9ac04..44186568a6 100644
--- a/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/ClassAnalyzerTest.java
+++ b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/ClassAnalyzerTest.java
@@ -21,6 +21,7 @@
import org.junit.Test;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.tree.MethodNode;
/**
* Unit tests for {@link ClassAnalyzer}.
@@ -39,27 +40,48 @@ public void setup() {
}
@Test(expected = IllegalStateException.class)
- public void testAnalyzeInstrumentedClass1() {
+ public void should_throw_IllegalStateException_when_class_is_instrumented_with_data_field() {
analyzer.visitField(InstrSupport.DATAFIELD_ACC,
InstrSupport.DATAFIELD_NAME, InstrSupport.DATAFIELD_DESC, null,
null);
}
@Test(expected = IllegalStateException.class)
- public void testAnalyzeInstrumentedClass2() {
+ public void should_throw_IllegalStateException_when_class_is_instrumented_with_init_method() {
analyzer.visitMethod(InstrSupport.INITMETHOD_ACC,
InstrSupport.INITMETHOD_NAME, InstrSupport.INITMETHOD_DESC,
null, null);
}
+ /**
+ * @see #should_add_non_empty_methods()
+ */
@Test
- public void testMethodFilter_Empty() {
- final MethodProbesVisitor mv = analyzer.visitMethod(0, "foo", "()V",
- null, null);
- mv.visitEnd();
+ public void should_not_add_empty_methods() {
+ final MethodNode m = new MethodNode(0, "foo", "()V", null, null);
+
+ final MethodProbesVisitor mv = analyzer.visitMethod(m.access, m.name,
+ m.desc, m.signature, m.exceptions.toArray(new String[0]));
+ mv.accept(m, mv);
+
assertEquals(0, coverage.getMethods().size());
}
+ /**
+ * @see #should_not_add_empty_methods()
+ */
+ @Test
+ public void should_add_non_empty_methods() {
+ final MethodNode m = new MethodNode(0, "foo", "()V", null, null);
+ m.visitInsn(Opcodes.RETURN);
+
+ final MethodProbesVisitor mv = analyzer.visitMethod(m.access, m.name,
+ m.desc, m.signature, m.exceptions.toArray(new String[0]));
+ mv.accept(m, mv);
+
+ assertEquals(1, coverage.getMethods().size());
+ }
+
@Test
public void should_collect_annotations() {
assertTrue(analyzer.getClassAnnotations().isEmpty());
From d867bbcfab8edaba0b41b2ca233f52e4dffaee82 Mon Sep 17 00:00:00 2001
From: Evgeny Mandrikov <138671+Godin@users.noreply.github.com>
Date: Mon, 12 Aug 2024 12:41:10 +0200
Subject: [PATCH 080/255] NormalizedFileNames should add prefix instead of
suffix (#1660)
Otherwise it changes file extensions.
---
org.jacoco.doc/docroot/doc/changes.html | 3 +++
.../internal/NormalizedFileNamesTest.java | 24 +++++++++----------
.../report/internal/NormalizedFileNames.java | 4 ++--
3 files changed, 17 insertions(+), 14 deletions(-)
diff --git a/org.jacoco.doc/docroot/doc/changes.html b/org.jacoco.doc/docroot/doc/changes.html
index 4bddc1488a..27af4034e4 100644
--- a/org.jacoco.doc/docroot/doc/changes.html
+++ b/org.jacoco.doc/docroot/doc/changes.html
@@ -42,6 +42,9 @@
Fixed bugs
Fixed interpretation of Kotlin SMAP
(GitHub #1525).
+
File extensions are preserved in HTML report in case of clashes of normalized
+ file names
+ (GitHub #1660).
Calculation of line coverage for Kotlin inline functions
+
Calculation of line coverage for Kotlin inline functions
(GitHub #1670).
+
Calculation of line coverage for Kotlin inline functions
+ with reified type parameter
+ (GitHub #1670,
+ #1700).
+
Calculation of coverage for Kotlin JvmSynthetic functions
+ (GitHub #1700).
Experimental support for Java 24 class files
(GitHub #1631).
Part of bytecode generated by the Kotlin Compose compiler plugin is
From 357a11dfce01095c8369bcdafec7d9a1251cf9fb Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 13 Sep 2024 09:07:52 +0000
Subject: [PATCH 091/255] Bump actions/setup-java from 4.2.2 to 4.3.0 (#1702)
Bumps [actions/setup-java](https://github.com/actions/setup-java) from 4.2.2 to 4.3.0.
- [Release notes](https://github.com/actions/setup-java/releases)
- [Commits](https://github.com/actions/setup-java/compare/6a0805fcefea3d4657a47ac4c165951e33482018...2dfa2011c5b2a0f1489bf9e433881c92c1631f88)
---
updated-dependencies:
- dependency-name: actions/setup-java
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
.github/workflows/ci.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 2f388c40cf..85d617e54e 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -24,7 +24,7 @@ jobs:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- - uses: actions/setup-java@6a0805fcefea3d4657a47ac4c165951e33482018 # v4.2.2
+ - uses: actions/setup-java@2dfa2011c5b2a0f1489bf9e433881c92c1631f88 # v4.3.0
with:
distribution: 'zulu'
java-version: |
@@ -59,7 +59,7 @@ jobs:
runs-on: windows-2022
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- - uses: actions/setup-java@6a0805fcefea3d4657a47ac4c165951e33482018 # v4.2.2
+ - uses: actions/setup-java@2dfa2011c5b2a0f1489bf9e433881c92c1631f88 # v4.3.0
with:
distribution: 'zulu'
java-version: |
From 517d8e5dd523a43c73e42bc06b99de76290056c5 Mon Sep 17 00:00:00 2001
From: Evgeny Mandrikov <138671+Godin@users.noreply.github.com>
Date: Mon, 16 Sep 2024 10:21:26 +0200
Subject: [PATCH 092/255] Use Maven Wrapper (#1707)
---
.azure-pipelines/azure-pipelines.yml | 12 +-
.github/workflows/ci.yml | 4 +-
.mvn/wrapper/maven-wrapper.properties | 19 ++
mvnw | 259 ++++++++++++++++++++++++++
mvnw.cmd | 149 +++++++++++++++
5 files changed, 432 insertions(+), 11 deletions(-)
create mode 100644 .mvn/wrapper/maven-wrapper.properties
create mode 100755 mvnw
create mode 100644 mvnw.cmd
diff --git a/.azure-pipelines/azure-pipelines.yml b/.azure-pipelines/azure-pipelines.yml
index 2726d645ce..485fbb31e9 100644
--- a/.azure-pipelines/azure-pipelines.yml
+++ b/.azure-pipelines/azure-pipelines.yml
@@ -77,12 +77,6 @@ jobs:
" > toolchains.xml
displayName: Setup JDK
- - bash: |
- set -e
- mkdir .maven
- curl -L "https://archive.apache.org/dist/maven/maven-3/3.8.3/binaries/apache-maven-3.8.3-bin.tar.gz" -o .maven/maven.tar.gz
- tar -xzf .maven/maven.tar.gz -C .maven --strip-components 1
- displayName: Setup Maven
- bash: |
if [[ "$JDK_VERSION" -ge "17" ]]; then
export JAVA_HOME=$PWD/.jdk
@@ -90,17 +84,17 @@ jobs:
export JAVA_HOME=$JAVA_HOME_17_X64
fi
if [[ "$BUILD_SOURCEBRANCH" == "refs/heads/master" && "$JDK_VERSION" == "5" ]]; then
- .maven/bin/mvn -V -B -e --no-transfer-progress -f org.jacoco.build \
+ ./mvnw -V -B -e --no-transfer-progress -f org.jacoco.build \
verify -Djdk.version=$JDK_VERSION -Dbytecode.version=$JDK_VERSION \
deploy:deploy -DdeployAtEnd \
--toolchains=toolchains.xml --settings=.azure-pipelines/maven-settings.xml
elif [[ "$BUILD_SOURCEBRANCH" == "refs/heads/master" && "$JDK_VERSION" == "11" ]]; then
- .maven/bin/mvn -V -B -e --no-transfer-progress -f org.jacoco.build \
+ ./mvnw -V -B -e --no-transfer-progress -f org.jacoco.build \
verify -Djdk.version=$JDK_VERSION -Dbytecode.version=$JDK_VERSION \
sonar:sonar \
--toolchains=toolchains.xml --settings=.azure-pipelines/maven-settings.xml
else
- .maven/bin/mvn -V -B -e --no-transfer-progress \
+ ./mvnw -V -B -e --no-transfer-progress \
verify -Djdk.version=$JDK_VERSION -Dbytecode.version=$JDK_VERSION -Decj=${ECJ:-} \
--toolchains=toolchains.xml
fi
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 85d617e54e..f08ad9abec 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -51,7 +51,7 @@ jobs:
" > toolchains.xml
- name: 'Build'
run: |
- mvn -V -B -e --no-transfer-progress \
+ ./mvnw -V -B -e --no-transfer-progress \
verify -Djdk.version=${{ matrix.jdk }} -Dbytecode.version=${{ matrix.jdk }} \
${{ matrix.ecj && '-Decj' || ''}} \
--toolchains=toolchains.xml
@@ -88,7 +88,7 @@ jobs:
- name: 'Build'
shell: bash
run: |
- mvn -V -B -e --no-transfer-progress \
+ ./mvnw -V -B -e --no-transfer-progress \
verify -Djdk.version=6 -Dbytecode.version=5 \
--toolchains=toolchains.xml
- uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties
new file mode 100644
index 0000000000..4cb0f7a85a
--- /dev/null
+++ b/.mvn/wrapper/maven-wrapper.properties
@@ -0,0 +1,19 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+wrapperVersion=3.3.2
+distributionType=only-script
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.3/apache-maven-3.8.3-bin.zip
diff --git a/mvnw b/mvnw
new file mode 100755
index 0000000000..19529ddf8c
--- /dev/null
+++ b/mvnw
@@ -0,0 +1,259 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Apache Maven Wrapper startup batch script, version 3.3.2
+#
+# Optional ENV vars
+# -----------------
+# JAVA_HOME - location of a JDK home dir, required when download maven via java source
+# MVNW_REPOURL - repo url base for downloading maven distribution
+# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
+# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output
+# ----------------------------------------------------------------------------
+
+set -euf
+[ "${MVNW_VERBOSE-}" != debug ] || set -x
+
+# OS specific support.
+native_path() { printf %s\\n "$1"; }
+case "$(uname)" in
+CYGWIN* | MINGW*)
+ [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")"
+ native_path() { cygpath --path --windows "$1"; }
+ ;;
+esac
+
+# set JAVACMD and JAVACCMD
+set_java_home() {
+ # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched
+ if [ -n "${JAVA_HOME-}" ]; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ]; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ JAVACCMD="$JAVA_HOME/jre/sh/javac"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ JAVACCMD="$JAVA_HOME/bin/javac"
+
+ if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then
+ echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2
+ echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2
+ return 1
+ fi
+ fi
+ else
+ JAVACMD="$(
+ 'set' +e
+ 'unset' -f command 2>/dev/null
+ 'command' -v java
+ )" || :
+ JAVACCMD="$(
+ 'set' +e
+ 'unset' -f command 2>/dev/null
+ 'command' -v javac
+ )" || :
+
+ if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then
+ echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2
+ return 1
+ fi
+ fi
+}
+
+# hash string like Java String::hashCode
+hash_string() {
+ str="${1:-}" h=0
+ while [ -n "$str" ]; do
+ char="${str%"${str#?}"}"
+ h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296))
+ str="${str#?}"
+ done
+ printf %x\\n $h
+}
+
+verbose() { :; }
+[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; }
+
+die() {
+ printf %s\\n "$1" >&2
+ exit 1
+}
+
+trim() {
+ # MWRAPPER-139:
+ # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds.
+ # Needed for removing poorly interpreted newline sequences when running in more
+ # exotic environments such as mingw bash on Windows.
+ printf "%s" "${1}" | tr -d '[:space:]'
+}
+
+# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties
+while IFS="=" read -r key value; do
+ case "${key-}" in
+ distributionUrl) distributionUrl=$(trim "${value-}") ;;
+ distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;;
+ esac
+done <"${0%/*}/.mvn/wrapper/maven-wrapper.properties"
+[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties"
+
+case "${distributionUrl##*/}" in
+maven-mvnd-*bin.*)
+ MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/
+ case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in
+ *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;;
+ :Darwin*x86_64) distributionPlatform=darwin-amd64 ;;
+ :Darwin*arm64) distributionPlatform=darwin-aarch64 ;;
+ :Linux*x86_64*) distributionPlatform=linux-amd64 ;;
+ *)
+ echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2
+ distributionPlatform=linux-amd64
+ ;;
+ esac
+ distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip"
+ ;;
+maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;;
+*) MVN_CMD="mvn${0##*/mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;;
+esac
+
+# apply MVNW_REPOURL and calculate MAVEN_HOME
+# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/
+[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}"
+distributionUrlName="${distributionUrl##*/}"
+distributionUrlNameMain="${distributionUrlName%.*}"
+distributionUrlNameMain="${distributionUrlNameMain%-bin}"
+MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}"
+MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")"
+
+exec_maven() {
+ unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || :
+ exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD"
+}
+
+if [ -d "$MAVEN_HOME" ]; then
+ verbose "found existing MAVEN_HOME at $MAVEN_HOME"
+ exec_maven "$@"
+fi
+
+case "${distributionUrl-}" in
+*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;;
+*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;;
+esac
+
+# prepare tmp dir
+if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then
+ clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; }
+ trap clean HUP INT TERM EXIT
+else
+ die "cannot create temp dir"
+fi
+
+mkdir -p -- "${MAVEN_HOME%/*}"
+
+# Download and Install Apache Maven
+verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
+verbose "Downloading from: $distributionUrl"
+verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
+
+# select .zip or .tar.gz
+if ! command -v unzip >/dev/null; then
+ distributionUrl="${distributionUrl%.zip}.tar.gz"
+ distributionUrlName="${distributionUrl##*/}"
+fi
+
+# verbose opt
+__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR=''
+[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v
+
+# normalize http auth
+case "${MVNW_PASSWORD:+has-password}" in
+'') MVNW_USERNAME='' MVNW_PASSWORD='' ;;
+has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;;
+esac
+
+if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then
+ verbose "Found wget ... using wget"
+ wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl"
+elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then
+ verbose "Found curl ... using curl"
+ curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl"
+elif set_java_home; then
+ verbose "Falling back to use Java to download"
+ javaSource="$TMP_DOWNLOAD_DIR/Downloader.java"
+ targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName"
+ cat >"$javaSource" <<-END
+ public class Downloader extends java.net.Authenticator
+ {
+ protected java.net.PasswordAuthentication getPasswordAuthentication()
+ {
+ return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() );
+ }
+ public static void main( String[] args ) throws Exception
+ {
+ setDefault( new Downloader() );
+ java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() );
+ }
+ }
+ END
+ # For Cygwin/MinGW, switch paths to Windows format before running javac and java
+ verbose " - Compiling Downloader.java ..."
+ "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java"
+ verbose " - Running Downloader.java ..."
+ "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")"
+fi
+
+# If specified, validate the SHA-256 sum of the Maven distribution zip file
+if [ -n "${distributionSha256Sum-}" ]; then
+ distributionSha256Result=false
+ if [ "$MVN_CMD" = mvnd.sh ]; then
+ echo "Checksum validation is not supported for maven-mvnd." >&2
+ echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
+ exit 1
+ elif command -v sha256sum >/dev/null; then
+ if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c >/dev/null 2>&1; then
+ distributionSha256Result=true
+ fi
+ elif command -v shasum >/dev/null; then
+ if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then
+ distributionSha256Result=true
+ fi
+ else
+ echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2
+ echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
+ exit 1
+ fi
+ if [ $distributionSha256Result = false ]; then
+ echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2
+ echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2
+ exit 1
+ fi
+fi
+
+# unzip and move
+if command -v unzip >/dev/null; then
+ unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip"
+else
+ tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar"
+fi
+printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url"
+mv -- "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME"
+
+clean || :
+exec_maven "$@"
diff --git a/mvnw.cmd b/mvnw.cmd
new file mode 100644
index 0000000000..b150b91ed5
--- /dev/null
+++ b/mvnw.cmd
@@ -0,0 +1,149 @@
+<# : batch portion
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Apache Maven Wrapper startup batch script, version 3.3.2
+@REM
+@REM Optional ENV vars
+@REM MVNW_REPOURL - repo url base for downloading maven distribution
+@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
+@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output
+@REM ----------------------------------------------------------------------------
+
+@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0)
+@SET __MVNW_CMD__=
+@SET __MVNW_ERROR__=
+@SET __MVNW_PSMODULEP_SAVE=%PSModulePath%
+@SET PSModulePath=
+@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @(
+ IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B)
+)
+@SET PSModulePath=%__MVNW_PSMODULEP_SAVE%
+@SET __MVNW_PSMODULEP_SAVE=
+@SET __MVNW_ARG0_NAME__=
+@SET MVNW_USERNAME=
+@SET MVNW_PASSWORD=
+@IF NOT "%__MVNW_CMD__%"=="" (%__MVNW_CMD__% %*)
+@echo Cannot start maven from wrapper >&2 && exit /b 1
+@GOTO :EOF
+: end batch / begin powershell #>
+
+$ErrorActionPreference = "Stop"
+if ($env:MVNW_VERBOSE -eq "true") {
+ $VerbosePreference = "Continue"
+}
+
+# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties
+$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl
+if (!$distributionUrl) {
+ Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
+}
+
+switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) {
+ "maven-mvnd-*" {
+ $USE_MVND = $true
+ $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip"
+ $MVN_CMD = "mvnd.cmd"
+ break
+ }
+ default {
+ $USE_MVND = $false
+ $MVN_CMD = $script -replace '^mvnw','mvn'
+ break
+ }
+}
+
+# apply MVNW_REPOURL and calculate MAVEN_HOME
+# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/
+if ($env:MVNW_REPOURL) {
+ $MVNW_REPO_PATTERN = if ($USE_MVND) { "/org/apache/maven/" } else { "/maven/mvnd/" }
+ $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')"
+}
+$distributionUrlName = $distributionUrl -replace '^.*/',''
+$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$',''
+$MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain"
+if ($env:MAVEN_USER_HOME) {
+ $MAVEN_HOME_PARENT = "$env:MAVEN_USER_HOME/wrapper/dists/$distributionUrlNameMain"
+}
+$MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join ''
+$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME"
+
+if (Test-Path -Path "$MAVEN_HOME" -PathType Container) {
+ Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME"
+ Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
+ exit $?
+}
+
+if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) {
+ Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl"
+}
+
+# prepare tmp dir
+$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile
+$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir"
+$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null
+trap {
+ if ($TMP_DOWNLOAD_DIR.Exists) {
+ try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
+ catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
+ }
+}
+
+New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null
+
+# Download and Install Apache Maven
+Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
+Write-Verbose "Downloading from: $distributionUrl"
+Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
+
+$webclient = New-Object System.Net.WebClient
+if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) {
+ $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD)
+}
+[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
+$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null
+
+# If specified, validate the SHA-256 sum of the Maven distribution zip file
+$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum
+if ($distributionSha256Sum) {
+ if ($USE_MVND) {
+ Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties."
+ }
+ Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash
+ if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) {
+ Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property."
+ }
+}
+
+# unzip and move
+Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null
+Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null
+try {
+ Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null
+} catch {
+ if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) {
+ Write-Error "fail to move MAVEN_HOME"
+ }
+} finally {
+ try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
+ catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
+}
+
+Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
From 49ee4db49a3a0c4fdb0f376d729c064292fdc642 Mon Sep 17 00:00:00 2001
From: Evgeny Mandrikov <138671+Godin@users.noreply.github.com>
Date: Tue, 17 Sep 2024 06:47:44 +0200
Subject: [PATCH 093/255] Require at least Maven 3.9.9 for build (#1708)
---
.mvn/wrapper/maven-wrapper.properties | 2 +-
org.jacoco.build/pom.xml | 3 +-
org.jacoco.doc/docroot/doc/build.html | 62 ++++++++++-----------
org.jacoco.doc/docroot/doc/changes.html | 6 +-
org.jacoco.doc/docroot/doc/environment.html | 2 +-
5 files changed, 38 insertions(+), 37 deletions(-)
diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties
index 4cb0f7a85a..d58dfb70ba 100644
--- a/.mvn/wrapper/maven-wrapper.properties
+++ b/.mvn/wrapper/maven-wrapper.properties
@@ -16,4 +16,4 @@
# under the License.
wrapperVersion=3.3.2
distributionType=only-script
-distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.3/apache-maven-3.8.3-bin.zip
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip
diff --git a/org.jacoco.build/pom.xml b/org.jacoco.build/pom.xml
index c28ccd159f..6a24dc0a69 100644
--- a/org.jacoco.build/pom.xml
+++ b/org.jacoco.build/pom.xml
@@ -519,8 +519,7 @@
17
-
- [3.6.3,3.8.2),(3.8.2,)
+ 3.9.9The rules for repo1.maven.org are that pom.xml files should not include repository definitions.
diff --git a/org.jacoco.doc/docroot/doc/build.html b/org.jacoco.doc/docroot/doc/build.html
index efab7c1556..f524bdbce4 100644
--- a/org.jacoco.doc/docroot/doc/build.html
+++ b/org.jacoco.doc/docroot/doc/build.html
@@ -23,9 +23,8 @@
Build
The JaCoCo build is based on Maven and
can be locally executed on every machine with a proper
- environment setup. In particular you need at
- least Maven 3.6.3 and JDK 17
- installations. Developers are encouraged to run the build before every commit
+ environment setup. In particular you need JDK 17
+ installation. Developers are encouraged to run the build before every commit
to ensure consistency of the source tree.
@@ -33,12 +32,13 @@
Build
Running the Build
- The build can be started by executing the following command with
- ./org.jacoco.build/ as the working directory:
+ The build can be started by executing the
+ Maven Wrapper using following
+ command with ./org.jacoco.build/ as the working directory:
The JaCoCo build is based on Maven
- and requires at least Maven 3.6.3 and JDK 17.
+ and requires at least Maven 3.9.9 and JDK 17.
See the build description for details.
JaCoCo now officially supports Java 23
+ (GitHub #1757).
Calculation of line coverage for Kotlin inline functions
(GitHub #1670).
Calculation of line coverage for Kotlin inline functions
diff --git a/org.jacoco.doc/docroot/doc/environment.html b/org.jacoco.doc/docroot/doc/environment.html
index 140a5e9f01..0bbb5c6d69 100644
--- a/org.jacoco.doc/docroot/doc/environment.html
+++ b/org.jacoco.doc/docroot/doc/environment.html
@@ -69,7 +69,7 @@
JRE/JDK
The minimum supported JRE version for JaCoCo is Java 5. To guarantee
compatibility JaCoCo release builds should always be executed using JDK 5.
In addition we run builds with 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17
- 18, 19, 20, 21 and 22 JDKs.
+ 18, 19, 20, 21, 22 and 23 JDKs.
Does JaCoCo have a plug-in for [Eclipse|Netbeans|Whatever...]?
What Java versions are supported by JaCoCo?
- JaCoCo supports Java class files from version 1.0 to 22. However the minimum
+ JaCoCo supports Java class files from version 1.0 to 23. However the minimum
JRE version required by the JaCoCo runtime (e.g. the agent) and the JaCoCo
tools is 1.5. Also note that class files under test from version 1.6 and above
have to contain valid stackmap frames.
From 04d689e07e5613917d64a914d0bdf12477336a19 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 21 Oct 2024 09:33:26 +0000
Subject: [PATCH 122/255] Upgrade maven-javadoc-plugin to 3.10.1 (#1738)
Bumps [org.apache.maven.plugins:maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) from 3.10.0 to 3.10.1.
- [Release notes](https://github.com/apache/maven-javadoc-plugin/releases)
- [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.10.0...maven-javadoc-plugin-3.10.1)
---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-javadoc-plugin
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
org.jacoco.build/pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/org.jacoco.build/pom.xml b/org.jacoco.build/pom.xml
index a870bd4b54..90ad9e6889 100644
--- a/org.jacoco.build/pom.xml
+++ b/org.jacoco.build/pom.xml
@@ -352,7 +352,7 @@
org.apache.maven.pluginsmaven-javadoc-plugin
- 3.10.0
+ 3.10.1truefalse
From 3a544915a86c7cbaf0b296c95c24322310c5a75b Mon Sep 17 00:00:00 2001
From: Evgeny Mandrikov <138671+Godin@users.noreply.github.com>
Date: Tue, 22 Oct 2024 21:08:15 +0200
Subject: [PATCH 123/255] Upgrade maven-compiler-plugin to 3.12.1 (#1730)
---
.github/dependabot.yml | 6 ++++++
org.jacoco.build/pom.xml | 3 ++-
2 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index f49500be46..d5d2b4272d 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -23,6 +23,12 @@ updates:
# It is known that upgrade from current version requires additional changes:
- dependency-name: "org.apache.maven.plugins:maven-plugin-plugin"
- dependency-name: "org.apache.maven.plugins:maven-invoker-plugin"
+ # Because of
+ # https://github.com/apache/maven-compiler-plugin/blob/maven-compiler-plugin-3.13.0/pom.xml#L71
+ # https://github.com/codehaus-plexus/plexus-compiler/blob/plexus-compiler-2.15.0/plexus-compilers/plexus-compiler-javac/src/main/java/org/codehaus/plexus/compiler/javac/JavacCompiler.java#L149-L163
+ # requires javac version to be at least 6:
+ - dependency-name: "org.apache.maven.plugins:maven-compiler-plugin"
+ versions: ">=3.13.0"
# Requires tests to be executed with Java 6:
- dependency-name: "org.apache.maven.plugins:maven-surefire-plugin"
versions: ">=2.20.0"
diff --git a/org.jacoco.build/pom.xml b/org.jacoco.build/pom.xml
index 90ad9e6889..e41f899683 100644
--- a/org.jacoco.build/pom.xml
+++ b/org.jacoco.build/pom.xml
@@ -312,7 +312,8 @@
org.apache.maven.pluginsmaven-compiler-plugin
- 3.11.0
+
+ 3.12.1org.apache.maven.plugins
From eef103a35387855e5a4157e0aceab1d4428b5dd1 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 22 Oct 2024 22:34:05 +0200
Subject: [PATCH 124/255] Upgrade exec-maven-plugin to 3.5.0 (#1760)
Bumps [org.codehaus.mojo:exec-maven-plugin](https://github.com/mojohaus/exec-maven-plugin) from 3.4.1 to 3.5.0.
- [Release notes](https://github.com/mojohaus/exec-maven-plugin/releases)
- [Commits](https://github.com/mojohaus/exec-maven-plugin/compare/3.4.1...3.5.0)
---
updated-dependencies:
- dependency-name: org.codehaus.mojo:exec-maven-plugin
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
org.jacoco.build/pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/org.jacoco.build/pom.xml b/org.jacoco.build/pom.xml
index e41f899683..f167167e3b 100644
--- a/org.jacoco.build/pom.xml
+++ b/org.jacoco.build/pom.xml
@@ -434,7 +434,7 @@
org.codehaus.mojoexec-maven-plugin
- 3.4.1
+ 3.5.0
From 1a6ad9dcd6fbeedfc7cb098f23b15e227ed1b275 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 29 Oct 2024 23:03:43 +0100
Subject: [PATCH 125/255] Bump actions/checkout from 4.2.1 to 4.2.2 (#1761)
Bumps [actions/checkout](https://github.com/actions/checkout) from 4.2.1 to 4.2.2.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871...11bd71901bbe5b1630ceea73d27597364c9af683)
---
updated-dependencies:
- dependency-name: actions/checkout
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
.github/workflows/ci.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index eac5fb5c5a..4de0942972 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -23,7 +23,7 @@ jobs:
name: JDK ${{ matrix.jdk }}${{ matrix.ecj && ' with ECJ' || ''}}
runs-on: ubuntu-20.04
steps:
- - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/setup-java@b36c23c0d998641eff861008f374ee103c25ac73 # v4.4.0
with:
distribution: 'zulu'
@@ -58,7 +58,7 @@ jobs:
Windows:
runs-on: windows-2022
steps:
- - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/setup-java@b36c23c0d998641eff861008f374ee103c25ac73 # v4.4.0
with:
distribution: 'zulu'
From d5908a249db6aa76b14f9661bc7f40e41ec7bc0c Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 29 Oct 2024 22:33:26 +0000
Subject: [PATCH 126/255] Bump actions/setup-java from 4.4.0 to 4.5.0 (#1763)
Bumps [actions/setup-java](https://github.com/actions/setup-java) from 4.4.0 to 4.5.0.
- [Release notes](https://github.com/actions/setup-java/releases)
- [Commits](https://github.com/actions/setup-java/compare/b36c23c0d998641eff861008f374ee103c25ac73...8df1039502a15bceb9433410b1a100fbe190c53b)
---
updated-dependencies:
- dependency-name: actions/setup-java
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
.github/workflows/ci.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 4de0942972..089b7f2f39 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -24,7 +24,7 @@ jobs:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- - uses: actions/setup-java@b36c23c0d998641eff861008f374ee103c25ac73 # v4.4.0
+ - uses: actions/setup-java@8df1039502a15bceb9433410b1a100fbe190c53b # v4.5.0
with:
distribution: 'zulu'
java-version: |
@@ -59,7 +59,7 @@ jobs:
runs-on: windows-2022
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- - uses: actions/setup-java@b36c23c0d998641eff861008f374ee103c25ac73 # v4.4.0
+ - uses: actions/setup-java@8df1039502a15bceb9433410b1a100fbe190c53b # v4.5.0
with:
distribution: 'zulu'
java-version: |
From bb9fdd670f2b5cf511520721461488cf1ecfc117 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 30 Oct 2024 09:49:41 +0000
Subject: [PATCH 127/255] Upgrade maven-dependency-plugin to 3.8.1 (#1762)
Bump org.apache.maven.plugins:maven-dependency-plugin
Bumps [org.apache.maven.plugins:maven-dependency-plugin](https://github.com/apache/maven-dependency-plugin) from 3.8.0 to 3.8.1.
- [Release notes](https://github.com/apache/maven-dependency-plugin/releases)
- [Commits](https://github.com/apache/maven-dependency-plugin/compare/maven-dependency-plugin-3.8.0...maven-dependency-plugin-3.8.1)
---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-dependency-plugin
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
org.jacoco.build/pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/org.jacoco.build/pom.xml b/org.jacoco.build/pom.xml
index f167167e3b..f074d89ad4 100644
--- a/org.jacoco.build/pom.xml
+++ b/org.jacoco.build/pom.xml
@@ -318,7 +318,7 @@
org.apache.maven.pluginsmaven-dependency-plugin
- 3.8.0
+ 3.8.1org.apache.maven.plugins
From 00312094ecf4ea543eeaf3beb7dcfdb932898405 Mon Sep 17 00:00:00 2001
From: Evgeny Mandrikov <138671+Godin@users.noreply.github.com>
Date: Fri, 8 Nov 2024 12:03:49 +0100
Subject: [PATCH 128/255] Add configuration files for Java code formatting in
IntelliJ IDEA (#1704)
* `.idea/externalDependencies.xml` to require installation of
EclipseCodeFormatter plugin into IntelliJ IDEA
* `.idea/eclipseCodeFormatter.xml` configures plugin to use
the same settings as for Eclipse IDE and spotelss-maven-plugin
* `.idea/codeStyles/codeStyleConfig.xml` and
`.idea/codeStyles/Project.xml` prevent use of wildcard imports
---
.idea/codeStyles/Project.xml | 8 ++++++++
.idea/codeStyles/codeStyleConfig.xml | 5 +++++
.idea/eclipseCodeFormatter.xml | 12 ++++++++++++
.idea/externalDependencies.xml | 6 ++++++
4 files changed, 31 insertions(+)
create mode 100644 .idea/codeStyles/Project.xml
create mode 100644 .idea/codeStyles/codeStyleConfig.xml
create mode 100644 .idea/eclipseCodeFormatter.xml
create mode 100644 .idea/externalDependencies.xml
diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
new file mode 100644
index 0000000000..a74a84d3a0
--- /dev/null
+++ b/.idea/codeStyles/Project.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml
new file mode 100644
index 0000000000..79ee123c2b
--- /dev/null
+++ b/.idea/codeStyles/codeStyleConfig.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/eclipseCodeFormatter.xml b/.idea/eclipseCodeFormatter.xml
new file mode 100644
index 0000000000..992c75f5c6
--- /dev/null
+++ b/.idea/eclipseCodeFormatter.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/externalDependencies.xml b/.idea/externalDependencies.xml
new file mode 100644
index 0000000000..0502dcf71c
--- /dev/null
+++ b/.idea/externalDependencies.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
From 11567593d0d1d5a9a5cc674baf4cf0e82c74966c Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 12 Nov 2024 22:12:38 +0000
Subject: [PATCH 129/255] Upgrade maven-javadoc-plugin to 3.11.1 (#1767)
Bump org.apache.maven.plugins:maven-javadoc-plugin in /org.jacoco.build
Bumps [org.apache.maven.plugins:maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) from 3.10.1 to 3.11.1.
- [Release notes](https://github.com/apache/maven-javadoc-plugin/releases)
- [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.10.1...maven-javadoc-plugin-3.11.1)
---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-javadoc-plugin
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
org.jacoco.build/pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/org.jacoco.build/pom.xml b/org.jacoco.build/pom.xml
index f074d89ad4..f5f451e58d 100644
--- a/org.jacoco.build/pom.xml
+++ b/org.jacoco.build/pom.xml
@@ -353,7 +353,7 @@
org.apache.maven.pluginsmaven-javadoc-plugin
- 3.10.1
+ 3.11.1truefalse
From 7e3207484e2df256838186be8c7ed5c72e8f93ac Mon Sep 17 00:00:00 2001
From: Evgeny Mandrikov <138671+Godin@users.noreply.github.com>
Date: Wed, 13 Nov 2024 07:59:28 +0100
Subject: [PATCH 130/255] Fix typo (#1775)
---
.../src/org/jacoco/core/internal/analysis/LineImplTest.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/LineImplTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/LineImplTest.java
index 1bba4f1109..fc35bd6293 100644
--- a/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/LineImplTest.java
+++ b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/LineImplTest.java
@@ -20,7 +20,7 @@
import org.junit.Test;
/**
- * Unit tests for {@link LineImplTest}.
+ * Unit tests for {@link LineImpl}.
*/
public class LineImplTest {
From a69d52865f34cd5cb1b7a673c1b57282dea74dec Mon Sep 17 00:00:00 2001
From: Evgeny Mandrikov <138671+Godin@users.noreply.github.com>
Date: Fri, 15 Nov 2024 12:48:00 +0100
Subject: [PATCH 131/255] Fix warning "'reportOutputDirectory' is unknown for
plugin 'maven-javadoc-plugin:3.11.1:jar (attach-javadocs)'" (#1776)
`reportOutputDirectory` parameter was removed in maven-javadoc-plugin
version 3.10.0 by
https://github.com/apache/maven-javadoc-plugin/commit/9638a6a76c6db53c3bf8ffbd5cec7318b0e25303
(https://issues.apache.org/jira/browse/MJAVADOC-785).
This was overlooked in commit 435579f27cb8259ba14c1a868245a843e053211f.
`outputDirectory` parameter can be used to restore prior behavior,
but this customization seems unnecessary.
---
org.jacoco.doc/pom.xml | 1 -
1 file changed, 1 deletion(-)
diff --git a/org.jacoco.doc/pom.xml b/org.jacoco.doc/pom.xml
index 6176308713..afd5f62766 100644
--- a/org.jacoco.doc/pom.xml
+++ b/org.jacoco.doc/pom.xml
@@ -198,7 +198,6 @@
jar
- ${project.build.directory}/apidocstrue*.internal.*,*.internal,org.jacoco.ant,org.jacoco.maven,org.jacoco.examples
From 3c44d5fd8d74f768301d9957237cf8db2052794b Mon Sep 17 00:00:00 2001
From: Evgeny Mandrikov <138671+Godin@users.noreply.github.com>
Date: Fri, 15 Nov 2024 22:52:12 +0100
Subject: [PATCH 132/255] KotlinWhenFilter should handle nullable enums (#1774)
---
.../targets/KotlinWhenExpressionTarget.kt | 33 +++
.../analysis/filter/KotlinWhenFilterTest.java | 198 ++++++++++++++++++
.../analysis/filter/KotlinWhenFilter.java | 45 +++-
org.jacoco.doc/docroot/doc/changes.html | 4 +
4 files changed, 276 insertions(+), 4 deletions(-)
diff --git a/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinWhenExpressionTarget.kt b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinWhenExpressionTarget.kt
index ccf98244bb..6f5b004b5c 100644
--- a/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinWhenExpressionTarget.kt
+++ b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinWhenExpressionTarget.kt
@@ -50,6 +50,27 @@ object KotlinWhenExpressionTarget {
else -> throw NoWhenBranchMatchedException() // assertEmpty()
} // assertFullyCovered()
+ private fun whenByNullableEnumWithNullCaseAndWithoutElse(e: Enum?): String =
+ when (e) { // assertFullyCovered(0, 3)
+ Enum.A -> "a" // assertFullyCovered()
+ Enum.B -> "b" // assertFullyCovered()
+ null -> "null" // assertFullyCovered()
+ } // assertFullyCovered()
+
+ private fun whenByNullableEnumWithoutNullCaseAndWithElse(e: Enum?): String =
+ when (e) { // assertFullyCovered(0, 3)
+ Enum.A -> "a" // assertFullyCovered()
+ Enum.B -> "b" // assertFullyCovered()
+ else -> "else" // assertFullyCovered()
+ } // assertFullyCovered()
+
+ private fun whenByNullableEnumWithNullAndElseCases(e: Enum?): String =
+ when (e) { // assertFullyCovered(0, 3)
+ Enum.A -> "a" // assertFullyCovered()
+ null -> "null" // assertFullyCovered()
+ else -> "else" // assertFullyCovered()
+ } // assertFullyCovered()
+
private fun whenString(p: String): Int = when (p) { // assertFullyCovered(0, 7)
"a" -> 1 // assertFullyCovered()
"b" -> 2 // assertFullyCovered()
@@ -88,6 +109,18 @@ object KotlinWhenExpressionTarget {
whenEnumRedundantElse(Enum.A)
whenEnumRedundantElse(Enum.B)
+ whenByNullableEnumWithNullCaseAndWithoutElse(Enum.A)
+ whenByNullableEnumWithNullCaseAndWithoutElse(Enum.B)
+ whenByNullableEnumWithNullCaseAndWithoutElse(null)
+
+ whenByNullableEnumWithoutNullCaseAndWithElse(Enum.A)
+ whenByNullableEnumWithoutNullCaseAndWithElse(Enum.B)
+ whenByNullableEnumWithoutNullCaseAndWithElse(null)
+
+ whenByNullableEnumWithNullAndElseCases(Enum.A)
+ whenByNullableEnumWithNullAndElseCases(Enum.B)
+ whenByNullableEnumWithNullAndElseCases(null)
+
whenString("")
whenString("a")
whenString("b")
diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinWhenFilterTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinWhenFilterTest.java
index bc366aac5a..77dcd4b583 100644
--- a/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinWhenFilterTest.java
+++ b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinWhenFilterTest.java
@@ -121,4 +121,202 @@ public void should_filter_implicit_default() {
assertReplacedBranches(switchNode, newTargets);
}
+ /**
+ *
+ * enum class E { A, B }
+ * fun example(e: E?) = when (e) {
+ * E.A -> "a"
+ * E.B -> "b"
+ * null -> "null"
+ * }
+ *
+ */
+ @Test
+ public void should_filter_when_by_nullable_enum_with_null_case_and_without_else() {
+ final Range range1 = new Range();
+ final Range range2 = new Range();
+ final HashSet newTargets = new HashSet();
+ final MethodNode m = new MethodNode(InstrSupport.ASM_API_VERSION, 0,
+ "example", "(LE;)Ljava/lang/String;", null, null);
+ final Label l1 = new Label();
+ final Label l2 = new Label();
+ final Label caseNull = new Label();
+ final Label caseElse = new Label();
+ final Label caseA = new Label();
+ final Label caseB = new Label();
+ final Label after = new Label();
+
+ m.visitVarInsn(Opcodes.ALOAD, 1);
+ m.visitInsn(Opcodes.DUP);
+ range1.fromInclusive = m.instructions.getLast();
+ m.visitJumpInsn(Opcodes.IFNONNULL, l1);
+ m.visitInsn(Opcodes.POP);
+ m.visitInsn(Opcodes.ICONST_M1);
+ m.visitJumpInsn(Opcodes.GOTO, l2);
+ m.visitLabel(l1);
+ m.visitFieldInsn(Opcodes.GETSTATIC, "ExampleKt$WhenMappings",
+ "$EnumSwitchMapping$0", "[I");
+ m.visitInsn(Opcodes.SWAP);
+ m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "ExampleKt$Enum", "ordinal",
+ "()I", false);
+ m.visitInsn(Opcodes.IALOAD);
+ m.visitLabel(l2);
+ range1.toInclusive = m.instructions.getLast();
+ m.visitTableSwitchInsn(-1, 2, caseElse, caseNull, caseElse, caseA,
+ caseB);
+ final AbstractInsnNode switchNode = m.instructions.getLast();
+
+ m.visitLabel(caseA);
+ m.visitLdcInsn("a");
+ newTargets.add(m.instructions.getLast());
+ m.visitJumpInsn(Opcodes.GOTO, after);
+
+ m.visitLabel(caseB);
+ m.visitLdcInsn("b");
+ newTargets.add(m.instructions.getLast());
+ m.visitJumpInsn(Opcodes.GOTO, after);
+
+ m.visitLabel(caseNull);
+ m.visitLdcInsn("null");
+ newTargets.add(m.instructions.getLast());
+ m.visitJumpInsn(Opcodes.GOTO, after);
+
+ m.visitLabel(caseElse);
+ range2.fromInclusive = m.instructions.getLast();
+ m.visitTypeInsn(Opcodes.NEW, "kotlin/NoWhenBranchMatchedException");
+ m.visitInsn(Opcodes.DUP);
+ m.visitMethodInsn(Opcodes.INVOKESPECIAL,
+ "kotlin/NoWhenBranchMatchedException", "", "()V", false);
+ m.visitInsn(Opcodes.ATHROW);
+ range2.toInclusive = m.instructions.getLast();
+
+ m.visitLabel(after);
+ m.visitInsn(Opcodes.ARETURN);
+
+ filter.filter(m, context, output);
+
+ assertIgnored(range1, range2);
+ assertReplacedBranches(switchNode, newTargets);
+ }
+
+ /**
+ *
+ * enum class E { A, B }
+ * fun example(e: E?) = when (e) {
+ * E.A -> "a"
+ * E.B -> "b"
+ * else -> "else"
+ * }
+ *
+ */
+ @Test
+ public void should_filter_when_by_nullable_enum_without_null_case_and_with_else() {
+ final Range range1 = new Range();
+ final MethodNode m = new MethodNode(InstrSupport.ASM_API_VERSION, 0,
+ "example", "(LE;)Ljava/lang/String;", null, null);
+ final Label l1 = new Label();
+ final Label l2 = new Label();
+ final Label caseElse = new Label();
+ final Label caseA = new Label();
+ final Label caseB = new Label();
+ final Label after = new Label();
+
+ m.visitVarInsn(Opcodes.ALOAD, 1);
+ m.visitInsn(Opcodes.DUP);
+ range1.fromInclusive = m.instructions.getLast();
+ m.visitJumpInsn(Opcodes.IFNONNULL, l1);
+ m.visitInsn(Opcodes.POP);
+ m.visitInsn(Opcodes.ICONST_M1);
+ m.visitJumpInsn(Opcodes.GOTO, l2);
+ m.visitLabel(l1);
+ m.visitFieldInsn(Opcodes.GETSTATIC, "ExampleKt$WhenMappings",
+ "$EnumSwitchMapping$0", "[I");
+ m.visitInsn(Opcodes.SWAP);
+ m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "ExampleKt$Enum", "ordinal",
+ "()I", false);
+ m.visitInsn(Opcodes.IALOAD);
+ m.visitLabel(l2);
+ range1.toInclusive = m.instructions.getLast();
+ m.visitTableSwitchInsn(1, 2, caseElse, caseA, caseB);
+
+ m.visitLabel(caseA);
+ m.visitLdcInsn("a");
+ m.visitJumpInsn(Opcodes.GOTO, after);
+
+ m.visitLabel(caseB);
+ m.visitLdcInsn("b");
+ m.visitJumpInsn(Opcodes.GOTO, after);
+
+ m.visitLabel(caseElse);
+ m.visitLdcInsn("else");
+
+ m.visitLabel(after);
+ m.visitInsn(Opcodes.ARETURN);
+
+ filter.filter(m, context, output);
+
+ assertIgnored(range1);
+ assertNoReplacedBranches();
+ }
+
+ /**
+ *
+ * enum class E { A, B }
+ * fun example(e: E?) = when (e) {
+ * E.A -> "a"
+ * null -> "null"
+ * else -> "else"
+ * }
+ *
+ */
+ @Test
+ public void should_filter_when_by_nullable_enum_with_null_and_else_cases() {
+ final Range range1 = new Range();
+ final MethodNode m = new MethodNode(InstrSupport.ASM_API_VERSION, 0,
+ "example", "(LE;)Ljava/lang/String;", null, null);
+ final Label l1 = new Label();
+ final Label l2 = new Label();
+ final Label caseNull = new Label();
+ final Label caseElse = new Label();
+ final Label caseA = new Label();
+ final Label after = new Label();
+
+ m.visitVarInsn(Opcodes.ALOAD, 1);
+ m.visitInsn(Opcodes.DUP);
+ range1.fromInclusive = m.instructions.getLast();
+ m.visitJumpInsn(Opcodes.IFNONNULL, l1);
+ m.visitInsn(Opcodes.POP);
+ m.visitInsn(Opcodes.ICONST_M1);
+ m.visitJumpInsn(Opcodes.GOTO, l2);
+ m.visitLabel(l1);
+ m.visitFieldInsn(Opcodes.GETSTATIC, "ExampleKt$WhenMappings",
+ "$EnumSwitchMapping$0", "[I");
+ m.visitInsn(Opcodes.SWAP);
+ m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "ExampleKt$Enum", "ordinal",
+ "()I", false);
+ m.visitInsn(Opcodes.IALOAD);
+ m.visitLabel(l2);
+ range1.toInclusive = m.instructions.getLast();
+ m.visitTableSwitchInsn(-1, 1, caseElse, caseNull, caseA);
+
+ m.visitLabel(caseA);
+ m.visitLdcInsn("a");
+ m.visitJumpInsn(Opcodes.GOTO, after);
+
+ m.visitLabel(caseNull);
+ m.visitLdcInsn("null");
+ m.visitJumpInsn(Opcodes.GOTO, after);
+
+ m.visitLabel(caseElse);
+ m.visitLdcInsn("else");
+
+ m.visitLabel(after);
+ m.visitInsn(Opcodes.ARETURN);
+
+ filter.filter(m, context, output);
+
+ assertIgnored(range1);
+ assertNoReplacedBranches();
+ }
+
}
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinWhenFilter.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinWhenFilter.java
index 354161918e..202ed3bea6 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinWhenFilter.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinWhenFilter.java
@@ -18,6 +18,7 @@
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
+import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.JumpInsnNode;
import org.objectweb.asm.tree.LabelNode;
import org.objectweb.asm.tree.LookupSwitchInsnNode;
@@ -26,9 +27,8 @@
/**
* Filters bytecode that Kotlin compiler generates for when
- * expressions which list all cases of enum or
- * sealed class, i.e. which don't require explicit
- * else.
+ * expressions and statements with subject of type enum class or
+ * sealed class.
*/
public final class KotlinWhenFilter implements IFilter {
@@ -39,6 +39,7 @@ public void filter(final MethodNode methodNode,
final Matcher matcher = new Matcher();
for (final AbstractInsnNode i : methodNode.instructions) {
matcher.match(i, output);
+ matcher.matchNullableEnum(i, output);
}
}
@@ -69,6 +70,39 @@ void match(final AbstractInsnNode start, final IFilterOutput output) {
}
}
}
+
+ void matchNullableEnum(final AbstractInsnNode start,
+ final IFilterOutput output) {
+ if (start.getOpcode() != Opcodes.DUP) {
+ return;
+ }
+ cursor = start;
+ // https://github.com/JetBrains/kotlin/blob/v2.0.0/compiler/backend/src/org/jetbrains/kotlin/codegen/when/EnumSwitchCodegen.java#L46
+ nextIs(Opcodes.IFNONNULL);
+ final JumpInsnNode jump1 = (JumpInsnNode) cursor;
+ nextIs(Opcodes.POP);
+ nextIs(Opcodes.ICONST_M1);
+ nextIs(Opcodes.GOTO);
+ final JumpInsnNode jump2 = (JumpInsnNode) cursor;
+ nextIs(Opcodes.GETSTATIC);
+ final FieldInsnNode fieldInsnNode = (FieldInsnNode) cursor;
+ // https://github.com/JetBrains/kotlin/blob/v2.0.0/compiler/backend/src/org/jetbrains/kotlin/codegen/when/WhenByEnumsMapping.java#L27-L28
+ if (fieldInsnNode == null
+ || !fieldInsnNode.owner.endsWith("$WhenMappings")
+ || !fieldInsnNode.name.startsWith("$EnumSwitchMapping$")) {
+ return;
+ }
+ nextIs(Opcodes.SWAP);
+ nextIs(Opcodes.INVOKEVIRTUAL); // ordinal()I
+ nextIs(Opcodes.IALOAD);
+ nextIsSwitch();
+ if (cursor != null
+ && skipNonOpcodes(jump1.label) == skipNonOpcodes(
+ jump2.getNext())
+ && skipNonOpcodes(jump2.label) == cursor) {
+ output.ignore(start, cursor.getPrevious());
+ }
+ }
}
private static LabelNode getDefaultLabel(final AbstractInsnNode i) {
@@ -90,9 +124,12 @@ private static void ignoreDefaultBranch(final AbstractInsnNode switchNode,
} else {
labels = ((TableSwitchInsnNode) switchNode).labels;
}
+ final LabelNode defaultLabel = getDefaultLabel(switchNode);
final Set newTargets = new HashSet();
for (final LabelNode label : labels) {
- newTargets.add(AbstractMatcher.skipNonOpcodes(label));
+ if (label != defaultLabel) {
+ newTargets.add(AbstractMatcher.skipNonOpcodes(label));
+ }
}
output.replaceBranches(switchNode, newTargets);
}
diff --git a/org.jacoco.doc/docroot/doc/changes.html b/org.jacoco.doc/docroot/doc/changes.html
index 144fe37ec9..2a5caaa836 100644
--- a/org.jacoco.doc/docroot/doc/changes.html
+++ b/org.jacoco.doc/docroot/doc/changes.html
@@ -43,6 +43,10 @@
New Features
Part of bytecode generated by the Kotlin compiler for suspending lambdas
without suspension points is filtered out during generation of report
(GitHub #1283).
+
Part of bytecode generated by the Kotlin compiler for when
+ expressions and statements with nullable enum subject is filtered out during
+ generation of report
+ (GitHub #1774).
Method getEntries generated by the Kotlin compiler for enum
classes is filtered out during generation of report
(GitHub #1625).
From c533dedc7cc6d1408836059c01459a8487db6485 Mon Sep 17 00:00:00 2001
From: Evgeny Mandrikov <138671+Godin@users.noreply.github.com>
Date: Sat, 16 Nov 2024 07:40:53 +0100
Subject: [PATCH 133/255] Fix typo (#1777)
---
.../src/org/jacoco/core/internal/flow/LabelInfo.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
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 48e813bd79..e3217d3e6d 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
@@ -119,7 +119,7 @@ public static void setMethodInvocationLine(final Label label) {
}
/**
- * Checks whether the a given label has been marked as a line with method
+ * Checks whether the given label has been marked as a line with method
* invocations.
*
* @param label
From 9368edebe136dba2477ad05a05873c08842f3b6b Mon Sep 17 00:00:00 2001
From: Evgeny Mandrikov <138671+Godin@users.noreply.github.com>
Date: Mon, 18 Nov 2024 08:31:24 +0100
Subject: [PATCH 134/255] Split KotlinWhenExpressionTarget (#1778)
---
...ssionTest.java => KotlinWhenEnumTest.java} | 10 +--
.../kotlin/KotlinWhenSealedTest.java | 27 ++++++++
.../kotlin/KotlinWhenStringTest.java | 27 ++++++++
...ssionTarget.kt => KotlinWhenEnumTarget.kt} | 66 +------------------
.../kotlin/targets/KotlinWhenSealedTarget.kt | 46 +++++++++++++
.../kotlin/targets/KotlinWhenStringTarget.kt | 62 +++++++++++++++++
6 files changed, 169 insertions(+), 69 deletions(-)
rename org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/{KotlinWhenExpressionTest.java => KotlinWhenEnumTest.java} (77%)
create mode 100644 org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinWhenSealedTest.java
create mode 100644 org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinWhenStringTest.java
rename org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/{KotlinWhenExpressionTarget.kt => KotlinWhenEnumTarget.kt} (54%)
create mode 100644 org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinWhenSealedTarget.kt
create mode 100644 org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinWhenStringTarget.kt
diff --git a/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinWhenExpressionTest.java b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinWhenEnumTest.java
similarity index 77%
rename from org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinWhenExpressionTest.java
rename to org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinWhenEnumTest.java
index 5bfe46d5d5..4aae895a46 100644
--- a/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinWhenExpressionTest.java
+++ b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinWhenEnumTest.java
@@ -13,15 +13,15 @@
package org.jacoco.core.test.validation.kotlin;
import org.jacoco.core.test.validation.ValidationTestBase;
-import org.jacoco.core.test.validation.kotlin.targets.KotlinWhenExpressionTarget;
+import org.jacoco.core.test.validation.kotlin.targets.KotlinWhenEnumTarget;
/**
- * Test of when expressions.
+ * Test of code coverage in {@link KotlinWhenEnumTarget}.
*/
-public class KotlinWhenExpressionTest extends ValidationTestBase {
+public class KotlinWhenEnumTest extends ValidationTestBase {
- public KotlinWhenExpressionTest() {
- super(KotlinWhenExpressionTarget.class);
+ public KotlinWhenEnumTest() {
+ super(KotlinWhenEnumTarget.class);
}
}
diff --git a/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinWhenSealedTest.java b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinWhenSealedTest.java
new file mode 100644
index 0000000000..25424b0dd6
--- /dev/null
+++ b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinWhenSealedTest.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.KotlinWhenSealedTarget;
+
+/**
+ * Test of code coverage in {@link KotlinWhenSealedTarget}.
+ */
+public class KotlinWhenSealedTest extends ValidationTestBase {
+
+ public KotlinWhenSealedTest() {
+ super(KotlinWhenSealedTarget.class);
+ }
+
+}
diff --git a/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinWhenStringTest.java b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinWhenStringTest.java
new file mode 100644
index 0000000000..404931faba
--- /dev/null
+++ b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinWhenStringTest.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.KotlinWhenStringTarget;
+
+/**
+ * Test of code coverage in {@link KotlinWhenStringTarget}.
+ */
+public class KotlinWhenStringTest extends ValidationTestBase {
+
+ public KotlinWhenStringTest() {
+ super(KotlinWhenStringTarget.class);
+ }
+
+}
diff --git a/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinWhenExpressionTarget.kt b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinWhenEnumTarget.kt
similarity index 54%
rename from org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinWhenExpressionTarget.kt
rename to org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinWhenEnumTarget.kt
index 6f5b004b5c..5983d5b3ce 100644
--- a/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinWhenExpressionTarget.kt
+++ b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinWhenEnumTarget.kt
@@ -13,26 +13,9 @@
package org.jacoco.core.test.validation.kotlin.targets
/**
- * This test target is `when` expression.
+ * Test target with `when` expressions with subject of type `enum class`.
*/
-object KotlinWhenExpressionTarget {
-
- private sealed class Sealed {
- object Sealed1 : Sealed()
- object Sealed2 : Sealed()
- }
-
- private fun whenSealed(p: Sealed): Int = when (p) { // assertFullyCovered()
- is Sealed.Sealed1 -> 1 // assertFullyCovered(0, 2)
- is Sealed.Sealed2 -> 2 // assertFullyCovered()
- } // assertFullyCovered()
-
- @Suppress("REDUNDANT_ELSE_IN_WHEN")
- private fun whenSealedRedundantElse(p: Sealed): Int = when (p) { // assertFullyCovered()
- is Sealed.Sealed1 -> 1 // assertFullyCovered(0, 2)
- is Sealed.Sealed2 -> 2 // assertFullyCovered(0, 0)
- else -> throw NoWhenBranchMatchedException() // assertEmpty()
- } // assertFullyCovered()
+object KotlinWhenEnumTarget {
private enum class Enum {
A, B
@@ -71,38 +54,8 @@ object KotlinWhenExpressionTarget {
else -> "else" // assertFullyCovered()
} // assertFullyCovered()
- private fun whenString(p: String): Int = when (p) { // assertFullyCovered(0, 7)
- "a" -> 1 // assertFullyCovered()
- "b" -> 2 // assertFullyCovered()
- "c" -> 3 // assertFullyCovered()
- "\u0000a" -> 4 // assertFullyCovered()
- "\u0000b" -> 5 // assertFullyCovered()
- "\u0000c" -> 6 // assertFullyCovered()
- else -> 7 // assertFullyCovered()
- } // assertFullyCovered()
-
- /**
- * Unlike [whenString]
- * in this example first case is the only case with biggest hashCode value.
- * FIXME https://github.com/jacoco/jacoco/issues/1295
- */
- private fun whenStringBiggestHashCodeFirst(p: String): Int = when (p) { // assertPartlyCovered(3, 11)
- "c" -> 1 // assertFullyCovered()
- "b" -> 2 // assertFullyCovered()
- "\u0000b" -> 3 // assertFullyCovered()
- "a" -> 4 // assertFullyCovered()
- "\u0000a" -> 5 // assertFullyCovered()
- else -> 6 // assertFullyCovered()
- } // assertFullyCovered()
-
@JvmStatic
fun main(args: Array) {
- whenSealed(Sealed.Sealed1)
- whenSealed(Sealed.Sealed2)
-
- whenSealedRedundantElse(Sealed.Sealed1)
- whenSealedRedundantElse(Sealed.Sealed2)
-
whenEnum(Enum.A)
whenEnum(Enum.B)
@@ -120,21 +73,6 @@ object KotlinWhenExpressionTarget {
whenByNullableEnumWithNullAndElseCases(Enum.A)
whenByNullableEnumWithNullAndElseCases(Enum.B)
whenByNullableEnumWithNullAndElseCases(null)
-
- whenString("")
- whenString("a")
- whenString("b")
- whenString("c")
- whenString("\u0000a")
- whenString("\u0000b")
- whenString("\u0000c")
-
- whenStringBiggestHashCodeFirst("")
- whenStringBiggestHashCodeFirst("a")
- whenStringBiggestHashCodeFirst("b")
- whenStringBiggestHashCodeFirst("c")
- whenStringBiggestHashCodeFirst("\u0000a")
- whenStringBiggestHashCodeFirst("\u0000b")
}
}
diff --git a/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinWhenSealedTarget.kt b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinWhenSealedTarget.kt
new file mode 100644
index 0000000000..12ec77ab52
--- /dev/null
+++ b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinWhenSealedTarget.kt
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * 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
+
+/**
+ * Test target with `when` expressions with subject of type `enum class`.
+ */
+object KotlinWhenSealedTarget {
+
+ private sealed class Sealed {
+ object Sealed1 : Sealed()
+ object Sealed2 : Sealed()
+ }
+
+ private fun whenSealed(p: Sealed): Int = when (p) { // assertFullyCovered()
+ is Sealed.Sealed1 -> 1 // assertFullyCovered(0, 2)
+ is Sealed.Sealed2 -> 2 // assertFullyCovered()
+ } // assertFullyCovered()
+
+ @Suppress("REDUNDANT_ELSE_IN_WHEN")
+ private fun whenSealedRedundantElse(p: Sealed): Int = when (p) { // assertFullyCovered()
+ is Sealed.Sealed1 -> 1 // assertFullyCovered(0, 2)
+ is Sealed.Sealed2 -> 2 // assertFullyCovered(0, 0)
+ else -> throw NoWhenBranchMatchedException() // assertEmpty()
+ } // assertFullyCovered()
+
+ @JvmStatic
+ fun main(args: Array) {
+ whenSealed(Sealed.Sealed1)
+ whenSealed(Sealed.Sealed2)
+
+ whenSealedRedundantElse(Sealed.Sealed1)
+ whenSealedRedundantElse(Sealed.Sealed2)
+ }
+
+}
diff --git a/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinWhenStringTarget.kt b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinWhenStringTarget.kt
new file mode 100644
index 0000000000..234c141221
--- /dev/null
+++ b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinWhenStringTarget.kt
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * 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
+
+/**
+ * Test target with `when` expressions with subject of type `String`.
+ */
+object KotlinWhenStringTarget {
+
+ private fun whenString(p: String): Int = when (p) { // assertFullyCovered(0, 7)
+ "a" -> 1 // assertFullyCovered()
+ "b" -> 2 // assertFullyCovered()
+ "c" -> 3 // assertFullyCovered()
+ "\u0000a" -> 4 // assertFullyCovered()
+ "\u0000b" -> 5 // assertFullyCovered()
+ "\u0000c" -> 6 // assertFullyCovered()
+ else -> 7 // assertFullyCovered()
+ } // assertFullyCovered()
+
+ /**
+ * Unlike [whenString]
+ * in this example first case is the only case with biggest hashCode value.
+ * FIXME https://github.com/jacoco/jacoco/issues/1295
+ */
+ private fun whenStringBiggestHashCodeFirst(p: String): Int = when (p) { // assertPartlyCovered(3, 11)
+ "c" -> 1 // assertFullyCovered()
+ "b" -> 2 // assertFullyCovered()
+ "\u0000b" -> 3 // assertFullyCovered()
+ "a" -> 4 // assertFullyCovered()
+ "\u0000a" -> 5 // assertFullyCovered()
+ else -> 6 // assertFullyCovered()
+ } // assertFullyCovered()
+
+ @JvmStatic
+ fun main(args: Array) {
+ whenString("")
+ whenString("a")
+ whenString("b")
+ whenString("c")
+ whenString("\u0000a")
+ whenString("\u0000b")
+ whenString("\u0000c")
+
+ whenStringBiggestHashCodeFirst("")
+ whenStringBiggestHashCodeFirst("a")
+ whenStringBiggestHashCodeFirst("b")
+ whenStringBiggestHashCodeFirst("c")
+ whenStringBiggestHashCodeFirst("\u0000a")
+ whenStringBiggestHashCodeFirst("\u0000b")
+ }
+
+}
From f34c838f1c31531b60d905607f35d531ddaacae1 Mon Sep 17 00:00:00 2001
From: Evgeny Mandrikov <138671+Godin@users.noreply.github.com>
Date: Mon, 18 Nov 2024 09:00:28 +0100
Subject: [PATCH 135/255] Validation tests should create ASMified and TEXTified
bytecode representations (#1770)
---
.../test/validation/ValidationTestBase.java | 22 +++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/org.jacoco.core.test/src/org/jacoco/core/test/validation/ValidationTestBase.java b/org.jacoco.core.test/src/org/jacoco/core/test/validation/ValidationTestBase.java
index 50ccd23e15..e3417a3d7a 100644
--- a/org.jacoco.core.test/src/org/jacoco/core/test/validation/ValidationTestBase.java
+++ b/org.jacoco.core.test/src/org/jacoco/core/test/validation/ValidationTestBase.java
@@ -17,6 +17,7 @@
import java.io.File;
import java.io.IOException;
+import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
@@ -43,6 +44,10 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runners.model.MultipleFailureException;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.util.ASMifier;
+import org.objectweb.asm.util.Textifier;
+import org.objectweb.asm.util.TraceClassVisitor;
/**
* Base class for validation tests. It executes the given class under code
@@ -107,6 +112,23 @@ private void analyze(final Analyzer analyzer, final ExecutionData data)
final byte[] bytes = TargetLoader
.getClassDataAsBytes(target.getClassLoader(), data.getName());
analyzer.analyzeClass(bytes, data.getName());
+ saveBytecodeRepresentations(bytes, data.getName());
+ }
+
+ private void saveBytecodeRepresentations(final byte[] classBytes,
+ final String className) throws IOException {
+ final File outputDir = new File("target/asm/" + target.getSimpleName());
+ outputDir.mkdirs();
+ final String fileName = className.replace('/', '.');
+ final PrintWriter textWriter = new PrintWriter(
+ new File(outputDir, fileName + ".txt"));
+ final PrintWriter asmWriter = new PrintWriter(
+ new File(outputDir, fileName + ".java"));
+ new ClassReader(classBytes).accept(new TraceClassVisitor(
+ new TraceClassVisitor(null, new Textifier(), textWriter),
+ new ASMifier(), asmWriter), 0);
+ textWriter.close();
+ asmWriter.close();
}
/**
From 1e8b179cc08dc00f8507b67db6582861fe3068f5 Mon Sep 17 00:00:00 2001
From: Evgeny Mandrikov <138671+Godin@users.noreply.github.com>
Date: Wed, 20 Nov 2024 08:24:40 +0100
Subject: [PATCH 136/255] Fix typo: replace "a instrumented" by "an
instrumented" (#1781)
"an" should be used instead of "a" when the
following word starts with a vowel sound.
---
.../core/test/perf/ExecuteInstrumentedCodeScenario.java | 2 +-
.../src/org/jacoco/core/instr/Instrumenter.java | 8 ++++----
.../org/jacoco/core/internal/instr/ClassInstrumenter.java | 2 +-
3 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/org.jacoco.core.test/src/org/jacoco/core/test/perf/ExecuteInstrumentedCodeScenario.java b/org.jacoco.core.test/src/org/jacoco/core/test/perf/ExecuteInstrumentedCodeScenario.java
index 0bd610fd98..54e455ab7d 100644
--- a/org.jacoco.core.test/src/org/jacoco/core/test/perf/ExecuteInstrumentedCodeScenario.java
+++ b/org.jacoco.core.test/src/org/jacoco/core/test/perf/ExecuteInstrumentedCodeScenario.java
@@ -22,7 +22,7 @@
/**
* This scenario runs a given scenario twice and reports the execution time:
- * Once on its original version, once in a instrumented version.
+ * Once on its original version, once in an instrumented version.
*/
public class ExecuteInstrumentedCodeScenario extends TimedScenario {
diff --git a/org.jacoco.core/src/org/jacoco/core/instr/Instrumenter.java b/org.jacoco.core/src/org/jacoco/core/instr/Instrumenter.java
index 7aaa4bb9d0..0b78892373 100644
--- a/org.jacoco.core/src/org/jacoco/core/instr/Instrumenter.java
+++ b/org.jacoco.core/src/org/jacoco/core/instr/Instrumenter.java
@@ -93,7 +93,7 @@ protected String getCommonSuperClass(final String type1,
}
/**
- * Creates a instrumented version of the given class if possible.
+ * Creates an instrumented version of the given class if possible.
*
* @param buffer
* definition of the class
@@ -113,7 +113,7 @@ public byte[] instrument(final byte[] buffer, final String name)
}
/**
- * Creates a instrumented version of the given class if possible. The
+ * Creates an instrumented version of the given class if possible. The
* provided {@link InputStream} is not closed by this method.
*
* @param input
@@ -137,7 +137,7 @@ public byte[] instrument(final InputStream input, final String name)
}
/**
- * Creates a instrumented version of the given class file. The provided
+ * Creates an instrumented version of the given class file. The provided
* {@link InputStream} and {@link OutputStream} instances are not closed by
* this method.
*
@@ -166,7 +166,7 @@ private IOException instrumentError(final String name,
}
/**
- * Creates a instrumented version of the given resource depending on its
+ * Creates an instrumented version of the given resource depending on its
* type. Class files and the content of archive files are instrumented. All
* other files are copied without modification. The provided
* {@link InputStream} and {@link OutputStream} instances are not closed by
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/instr/ClassInstrumenter.java b/org.jacoco.core/src/org/jacoco/core/internal/instr/ClassInstrumenter.java
index ff76b441c6..52847b2370 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/instr/ClassInstrumenter.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/instr/ClassInstrumenter.java
@@ -28,7 +28,7 @@ public class ClassInstrumenter extends ClassProbesVisitor {
private String className;
/**
- * Emits a instrumented version of this class to the given class visitor.
+ * Emits an instrumented version of this class to the given class visitor.
*
* @param probeArrayStrategy
* this strategy will be used to access the probe array
From 20f076cb921588b80e6bb0b397b9aaf4dde910b5 Mon Sep 17 00:00:00 2001
From: Evgeny Mandrikov <138671+Godin@users.noreply.github.com>
Date: Wed, 20 Nov 2024 20:49:24 +0100
Subject: [PATCH 137/255] Fix typo: replace "an child" by "a child" (#1782)
"a" should be used instead of "an" when the
following word doesn't start with a vowel sound.
---
.../src/org/jacoco/report/internal/xml/XMLElement.java | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/org.jacoco.report/src/org/jacoco/report/internal/xml/XMLElement.java b/org.jacoco.report/src/org/jacoco/report/internal/xml/XMLElement.java
index 7908970c90..950b377647 100644
--- a/org.jacoco.report/src/org/jacoco/report/internal/xml/XMLElement.java
+++ b/org.jacoco.report/src/org/jacoco/report/internal/xml/XMLElement.java
@@ -157,7 +157,7 @@ private void quote(final String text) throws IOException {
}
/**
- * Adds an attribute to this element. May only be called before an child
+ * Adds an attribute to this element. May only be called before a child
* element is added or this element has been closed. The attribute value
* will be quoted. If the value is null the attribute will not
* be added.
@@ -188,7 +188,7 @@ public final void attr(final String name, final String value)
}
/**
- * Adds an attribute to this element. May only be called before an child
+ * Adds an attribute to this element. May only be called before a child
* element is added or this element has been closed. The attribute value is
* the decimal representation of the given int value.
*
@@ -206,7 +206,7 @@ public final void attr(final String name, final int value)
}
/**
- * Adds an attribute to this element. May only be called before an child
+ * Adds an attribute to this element. May only be called before a child
* element is added or this element has been closed. The attribute value is
* the decimal representation of the given long value.
*
From 764b1d1026c9dc58028cfbb03a90f6048135ffcf Mon Sep 17 00:00:00 2001
From: Evgeny Mandrikov <138671+Godin@users.noreply.github.com>
Date: Thu, 21 Nov 2024 06:50:12 +0100
Subject: [PATCH 138/255] Fix typo (#1783)
---
.../src/org/jacoco/report/internal/html/page/SessionsPage.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/org.jacoco.report/src/org/jacoco/report/internal/html/page/SessionsPage.java b/org.jacoco.report/src/org/jacoco/report/internal/html/page/SessionsPage.java
index a51c3f7680..4ec8ad08d0 100644
--- a/org.jacoco.report/src/org/jacoco/report/internal/html/page/SessionsPage.java
+++ b/org.jacoco.report/src/org/jacoco/report/internal/html/page/SessionsPage.java
@@ -54,7 +54,7 @@ public class SessionsPage extends ReportPage {
private final ElementIndex index;
/**
- * Creates a new page page to display session information.
+ * Creates a new page to display session information.
*
* @param sessionInfos
* session info objects
From ed3cb650e1dd477fcdf8a74e799b60825e5a98a3 Mon Sep 17 00:00:00 2001
From: Evgeny Mandrikov <138671+Godin@users.noreply.github.com>
Date: Thu, 21 Nov 2024 21:43:21 +0100
Subject: [PATCH 139/255] Build should not produce warning about generation of
JUnit report (#1784)
---
org.jacoco.doc/pom.xml | 1 -
1 file changed, 1 deletion(-)
diff --git a/org.jacoco.doc/pom.xml b/org.jacoco.doc/pom.xml
index afd5f62766..cd4f5c1df1 100644
--- a/org.jacoco.doc/pom.xml
+++ b/org.jacoco.doc/pom.xml
@@ -157,7 +157,6 @@
-
From e68ecf2385522009dc74dace96037f8d90be311e Mon Sep 17 00:00:00 2001
From: "Marc R. Hoffmann"
Date: Thu, 21 Nov 2024 22:50:50 +0100
Subject: [PATCH 140/255] Do not test with SecurityManager when Java version 24
and above (#1780)
Co-authored-by: Evgeny Mandrikov
---
org.jacoco.ant.test/pom.xml | 4 ++
.../src/org/jacoco/ant/CoverageTaskTest.xml | 16 -------
.../src/org/jacoco/ant/InstrumentTaskTest.xml | 10 ++---
.../org/jacoco/ant/SecurityManagerTest.java | 41 ++++++++++++++++++
.../org/jacoco/ant/SecurityManagerTest.xml | 43 +++++++++++++++++++
.../src/org/jacoco/ant/empty.xml | 17 ++++++++
6 files changed, 110 insertions(+), 21 deletions(-)
create mode 100644 org.jacoco.ant.test/src/org/jacoco/ant/SecurityManagerTest.java
create mode 100644 org.jacoco.ant.test/src/org/jacoco/ant/SecurityManagerTest.xml
create mode 100644 org.jacoco.ant.test/src/org/jacoco/ant/empty.xml
diff --git a/org.jacoco.ant.test/pom.xml b/org.jacoco.ant.test/pom.xml
index 0decb5008b..8e33b6e2f2 100644
--- a/org.jacoco.ant.test/pom.xml
+++ b/org.jacoco.ant.test/pom.xml
@@ -33,6 +33,10 @@
${project.groupId}org.jacoco.ant
+
+ ${project.groupId}
+ org.jacoco.core.test
+ junitjunit
diff --git a/org.jacoco.ant.test/src/org/jacoco/ant/CoverageTaskTest.xml b/org.jacoco.ant.test/src/org/jacoco/ant/CoverageTaskTest.xml
index a4024d2ea7..3c7308632a 100644
--- a/org.jacoco.ant.test/src/org/jacoco/ant/CoverageTaskTest.xml
+++ b/org.jacoco.ant.test/src/org/jacoco/ant/CoverageTaskTest.xml
@@ -166,20 +166,4 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
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 8046900240..6fea583c16 100644
--- a/org.jacoco.ant.test/src/org/jacoco/ant/InstrumentTaskTest.xml
+++ b/org.jacoco.ant.test/src/org/jacoco/ant/InstrumentTaskTest.xml
@@ -63,7 +63,7 @@
-
+
@@ -84,7 +84,7 @@
-
+
@@ -95,7 +95,7 @@
-
+ destfile=test.exec
@@ -112,7 +112,7 @@
-
+
@@ -129,7 +129,7 @@
-
+
diff --git a/org.jacoco.ant.test/src/org/jacoco/ant/SecurityManagerTest.java b/org.jacoco.ant.test/src/org/jacoco/ant/SecurityManagerTest.java
new file mode 100644
index 0000000000..ef9207cef7
--- /dev/null
+++ b/org.jacoco.ant.test/src/org/jacoco/ant/SecurityManagerTest.java
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * 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:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.ant;
+
+import java.io.File;
+
+import org.apache.ant.antunit.junit3.AntUnitSuite;
+import org.apache.ant.antunit.junit4.AntUnitSuiteRunner;
+import org.jacoco.core.test.validation.JavaVersion;
+import org.junit.runner.RunWith;
+
+import junit.framework.TestSuite;
+
+@RunWith(AntUnitSuiteRunner.class)
+public class SecurityManagerTest {
+
+ public static TestSuite suite() {
+ if (JavaVersion.current().isBefore("24")) {
+ System.setProperty("org.jacoco.ant.securityManagerTest.classes.dir",
+ TestTarget.getClassPath());
+ final File file = new File(
+ "src/org/jacoco/ant/SecurityManagerTest.xml");
+ return new AntUnitSuite(file, SecurityManagerTest.class);
+ }
+ // Ability to enable Security Manager was removed in Java 24
+ // https://openjdk.org/jeps/486
+ final File file = new File("src/org/jacoco/ant/empty.xml");
+ return new AntUnitSuite(file, SecurityManagerTest.class);
+ }
+
+}
diff --git a/org.jacoco.ant.test/src/org/jacoco/ant/SecurityManagerTest.xml b/org.jacoco.ant.test/src/org/jacoco/ant/SecurityManagerTest.xml
new file mode 100644
index 0000000000..0ce62cffac
--- /dev/null
+++ b/org.jacoco.ant.test/src/org/jacoco/ant/SecurityManagerTest.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/org.jacoco.ant.test/src/org/jacoco/ant/empty.xml b/org.jacoco.ant.test/src/org/jacoco/ant/empty.xml
new file mode 100644
index 0000000000..70a5a1dded
--- /dev/null
+++ b/org.jacoco.ant.test/src/org/jacoco/ant/empty.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
From 92936c51fb3572f77a4eca9a2c1775afb7580dfa Mon Sep 17 00:00:00 2001
From: Evgeny Mandrikov <138671+Godin@users.noreply.github.com>
Date: Fri, 22 Nov 2024 08:22:26 +0100
Subject: [PATCH 141/255] SecurityManagerTest should not depend on
CoverageTaskTest (#1785)
---
org.jacoco.ant.test/src/org/jacoco/ant/SecurityManagerTest.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/org.jacoco.ant.test/src/org/jacoco/ant/SecurityManagerTest.xml b/org.jacoco.ant.test/src/org/jacoco/ant/SecurityManagerTest.xml
index 0ce62cffac..beec33cd44 100644
--- a/org.jacoco.ant.test/src/org/jacoco/ant/SecurityManagerTest.xml
+++ b/org.jacoco.ant.test/src/org/jacoco/ant/SecurityManagerTest.xml
@@ -27,7 +27,7 @@
-
+
From a1571a8d82b0a99df2131dfdfd6749605fee93db Mon Sep 17 00:00:00 2001
From: Evgeny Mandrikov <138671+Godin@users.noreply.github.com>
Date: Fri, 22 Nov 2024 09:07:07 +0100
Subject: [PATCH 142/255] Fix typo: replace "a instance" by "an instance"
(#1786)
"an" should be used instead of "a" when the
following word starts with a vowel sound.
---
.../src/org/jacoco/core/data/ExecutionDataStore.java | 2 +-
.../core/internal/analysis/MethodCoverageCalculator.java | 6 +++---
.../src/org/jacoco/report/internal/xml/XMLElement.java | 2 +-
3 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/org.jacoco.core/src/org/jacoco/core/data/ExecutionDataStore.java b/org.jacoco.core/src/org/jacoco/core/data/ExecutionDataStore.java
index 0b62309d4a..b6aad5268f 100644
--- a/org.jacoco.core/src/org/jacoco/core/data/ExecutionDataStore.java
+++ b/org.jacoco.core/src/org/jacoco/core/data/ExecutionDataStore.java
@@ -24,7 +24,7 @@
* {@link IExecutionDataVisitor} interface. If execution data is provided
* multiple times for the same class the data is merged, i.e. a probe is marked
* as executed if it is reported as executed at least once. This allows to merge
- * coverage date from multiple runs. A instance of this class is not thread
+ * coverage date from multiple runs. An instance of this class is not thread
* safe.
*/
public final class ExecutionDataStore implements IExecutionDataVisitor {
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/MethodCoverageCalculator.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/MethodCoverageCalculator.java
index 2c7dec6dac..1e0bbe4e5c 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/MethodCoverageCalculator.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/MethodCoverageCalculator.java
@@ -25,9 +25,9 @@
import org.objectweb.asm.tree.AbstractInsnNode;
/**
- * Calculates the filtered coverage of a single method. A instance of this class
- * can be first used as {@link IFilterOutput} before the coverage result is
- * calculated.
+ * Calculates the filtered coverage of a single method. An instance of this
+ * class can be first used as {@link IFilterOutput} before the coverage result
+ * is calculated.
*/
class MethodCoverageCalculator implements IFilterOutput {
diff --git a/org.jacoco.report/src/org/jacoco/report/internal/xml/XMLElement.java b/org.jacoco.report/src/org/jacoco/report/internal/xml/XMLElement.java
index 950b377647..4ecfdfcc05 100644
--- a/org.jacoco.report/src/org/jacoco/report/internal/xml/XMLElement.java
+++ b/org.jacoco.report/src/org/jacoco/report/internal/xml/XMLElement.java
@@ -246,7 +246,7 @@ public final void text(final String text) throws IOException {
/**
* Creates a new child element for this element. Might be overridden in
- * subclasses to return a instance of the subclass.
+ * subclasses to return an instance of the subclass.
*
* @param name
* name of the child element
From 91bf76cbbbd42d733e6b8bef7a094a01ecaccd74 Mon Sep 17 00:00:00 2001
From: Evgeny Mandrikov <138671+Godin@users.noreply.github.com>
Date: Fri, 22 Nov 2024 20:50:48 +0100
Subject: [PATCH 143/255] Fix typo: replace "an listener" by "a listener"
(#1789)
"a" should be used instead of "an" when the
following word doesn't start with a vowel sound.
---
.../src/org/jacoco/core/data/ExecutionDataReader.java | 4 ++--
.../src/org/jacoco/core/runtime/RemoteControlReader.java | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/org.jacoco.core/src/org/jacoco/core/data/ExecutionDataReader.java b/org.jacoco.core/src/org/jacoco/core/data/ExecutionDataReader.java
index f30a3c4d74..0f7aaf79a3 100644
--- a/org.jacoco.core/src/org/jacoco/core/data/ExecutionDataReader.java
+++ b/org.jacoco.core/src/org/jacoco/core/data/ExecutionDataReader.java
@@ -46,7 +46,7 @@ public ExecutionDataReader(final InputStream input) {
}
/**
- * Sets an listener for session information.
+ * Sets a listener for session information.
*
* @param visitor
* visitor to retrieve session info events
@@ -56,7 +56,7 @@ public void setSessionInfoVisitor(final ISessionInfoVisitor visitor) {
}
/**
- * Sets an listener for execution data.
+ * Sets a listener for execution data.
*
* @param visitor
* visitor to retrieve execution data events
diff --git a/org.jacoco.core/src/org/jacoco/core/runtime/RemoteControlReader.java b/org.jacoco.core/src/org/jacoco/core/runtime/RemoteControlReader.java
index d5b9c81785..7f4ff118db 100644
--- a/org.jacoco.core/src/org/jacoco/core/runtime/RemoteControlReader.java
+++ b/org.jacoco.core/src/org/jacoco/core/runtime/RemoteControlReader.java
@@ -50,7 +50,7 @@ protected boolean readBlock(final byte blockid) throws IOException {
}
/**
- * Sets an listener for agent commands.
+ * Sets a listener for agent commands.
*
* @param visitor
* visitor to retrieve agent commands
From 1d9f0bb2ec7bcf7c68466e43d911ab7b58fe2817 Mon Sep 17 00:00:00 2001
From: Evgeny Mandrikov <138671+Godin@users.noreply.github.com>
Date: Fri, 22 Nov 2024 22:19:58 +0100
Subject: [PATCH 144/255] Fix javadoc doclint warning "no @param for fragment"
(#1788)
This was overlooked in commit 831ecd609e17a3a5f8e114134fa0ce1d2071c6bd.
---
.../src/org/jacoco/core/internal/analysis/SourceNodeImpl.java | 2 ++
1 file changed, 2 insertions(+)
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/SourceNodeImpl.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/SourceNodeImpl.java
index bab8b9edfd..44149c62f1 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/SourceNodeImpl.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/SourceNodeImpl.java
@@ -42,6 +42,8 @@ public SourceNodeImpl(final ElementType elementType, final String name) {
}
/**
+ * @param fragment
+ * fragment to apply
* @return true if fragment contains lines of this node
*/
public boolean applyFragment(final SourceNodeImpl fragment) {
From 10705517b5d9a0bc8c8a9d3386c84adc0740112c Mon Sep 17 00:00:00 2001
From: Evgeny Mandrikov <138671+Godin@users.noreply.github.com>
Date: Mon, 25 Nov 2024 00:07:45 +0100
Subject: [PATCH 145/255] Add validation test for Kotlin `when` statement with
`sealed class` subject type (#1791)
---
.../kotlin/targets/KotlinWhenSealedTarget.kt | 73 +++++++++++++++----
1 file changed, 60 insertions(+), 13 deletions(-)
diff --git a/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinWhenSealedTarget.kt b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinWhenSealedTarget.kt
index 12ec77ab52..0b3b3d87ec 100644
--- a/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinWhenSealedTarget.kt
+++ b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinWhenSealedTarget.kt
@@ -12,8 +12,10 @@
*******************************************************************************/
package org.jacoco.core.test.validation.kotlin.targets
+import org.jacoco.core.test.validation.targets.Stubs.nop
+
/**
- * Test target with `when` expressions with subject of type `enum class`.
+ * Test target with `when` expressions and statements with subject of type `sealed class`.
*/
object KotlinWhenSealedTarget {
@@ -22,25 +24,70 @@ object KotlinWhenSealedTarget {
object Sealed2 : Sealed()
}
- private fun whenSealed(p: Sealed): Int = when (p) { // assertFullyCovered()
- is Sealed.Sealed1 -> 1 // assertFullyCovered(0, 2)
- is Sealed.Sealed2 -> 2 // assertFullyCovered()
- } // assertFullyCovered()
+ private fun expression(sealed: Sealed): String =
+ when (sealed) { // assertFullyCovered()
+ is Sealed.Sealed1 -> "case 1" // assertFullyCovered(0, 2)
+ is Sealed.Sealed2 -> "case 2" // assertFullyCovered()
+ } // assertFullyCovered()
@Suppress("REDUNDANT_ELSE_IN_WHEN")
- private fun whenSealedRedundantElse(p: Sealed): Int = when (p) { // assertFullyCovered()
- is Sealed.Sealed1 -> 1 // assertFullyCovered(0, 2)
- is Sealed.Sealed2 -> 2 // assertFullyCovered(0, 0)
- else -> throw NoWhenBranchMatchedException() // assertEmpty()
+ private fun expressionWithRedundantElse(sealed: Sealed): String =
+ when (sealed) { // assertFullyCovered()
+ is Sealed.Sealed1 -> "case 1" // assertFullyCovered(0, 2)
+ is Sealed.Sealed2 -> "case 2" // assertFullyCovered(0, 0)
+ else -> throw NoWhenBranchMatchedException() // assertEmpty()
+ } // assertFullyCovered()
+
+ /**
+ * Since Kotlin 1.7 `when` statement with subject of type `sealed class`
+ * must be exhaustive (error otherwise, warning in 1.6), however
+ * Kotlin compiler prior to version 2.0 was generating bytecode
+ * indistinguishable from [nonSealedWhen] and [nonSealedIf]
+ * that do not have coverage for the case of [NonSealed.NonSealed3].
+ */
+ private fun statement(sealed: Sealed) { // assertEmpty()
+ when (sealed) { // assertFullyCovered()
+ is Sealed.Sealed1 -> nop("case 1") // assertFullyCovered(0, 2)
+ is Sealed.Sealed2 -> nop("case 2") // assertFullyCovered()
+ } // assertEmpty()
+ } // assertFullyCovered()
+
+ private abstract class NonSealed {
+ class NonSealed1 : NonSealed()
+ class NonSealed2 : NonSealed()
+ class NonSealed3 : NonSealed()
+ }
+
+ private fun nonSealedWhen(nonSealed: NonSealed) { // assertEmpty()
+ when (nonSealed) { // assertFullyCovered()
+ is NonSealed.NonSealed1 -> nop("case 1") // assertFullyCovered(0, 2)
+ is NonSealed.NonSealed2 -> nop("case 2") // assertFullyCovered(1, 1)
+ /* missing is NonSealed.NonSealed3 */
+ } // assertEmpty()
+ } // assertFullyCovered()
+
+ private fun nonSealedIf(nonSealed: NonSealed) { // assertEmpty()
+ if (nonSealed is NonSealed.NonSealed1) nop("case 1") // assertFullyCovered(0, 2)
+ else if (nonSealed is NonSealed.NonSealed2) nop("case 2") // assertFullyCovered(1, 1)
+ /* missing is NonSealed.NonSealed3 */
} // assertFullyCovered()
@JvmStatic
fun main(args: Array) {
- whenSealed(Sealed.Sealed1)
- whenSealed(Sealed.Sealed2)
+ expression(Sealed.Sealed1)
+ expression(Sealed.Sealed2)
+
+ expressionWithRedundantElse(Sealed.Sealed1)
+ expressionWithRedundantElse(Sealed.Sealed2)
+
+ statement(Sealed.Sealed1)
+ statement(Sealed.Sealed2)
+
+ nonSealedWhen(NonSealed.NonSealed1())
+ nonSealedWhen(NonSealed.NonSealed2())
- whenSealedRedundantElse(Sealed.Sealed1)
- whenSealedRedundantElse(Sealed.Sealed2)
+ nonSealedIf(NonSealed.NonSealed1())
+ nonSealedIf(NonSealed.NonSealed2())
}
}
From 0ebc1d93eb58b451dae75c5825647fef270f70e5 Mon Sep 17 00:00:00 2001
From: Evgeny Mandrikov <138671+Godin@users.noreply.github.com>
Date: Mon, 25 Nov 2024 08:46:09 +0100
Subject: [PATCH 146/255] Fix typo: replace "a XML" by "an XML" (#1790)
"an" should be used instead of "a" when the
following word starts with a vowel sound.
---
org.jacoco.doc/docroot/doc/ant.html | 2 +-
.../src/org/jacoco/report/internal/xml/ReportElement.java | 2 +-
.../src/org/jacoco/report/internal/xml/XMLElement.java | 8 ++++----
3 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/org.jacoco.doc/docroot/doc/ant.html b/org.jacoco.doc/docroot/doc/ant.html
index f0ddfd58f5..1b1b37e39c 100644
--- a/org.jacoco.doc/docroot/doc/ant.html
+++ b/org.jacoco.doc/docroot/doc/ant.html
@@ -89,7 +89,7 @@
Prerequisites
- Declaring a XML namespace for JaCoCo tasks is optional but always recommended
+ Declaring an XML namespace for JaCoCo tasks is optional but always recommended
if you mix tasks from different libraries. All subsequent examples use the
jacoco prefix declared above. If you don't declare a separate
namespace the jacoco prefix must be removed from the following
diff --git a/org.jacoco.report/src/org/jacoco/report/internal/xml/ReportElement.java b/org.jacoco.report/src/org/jacoco/report/internal/xml/ReportElement.java
index 3e35fc129b..18fbd735f8 100644
--- a/org.jacoco.report/src/org/jacoco/report/internal/xml/ReportElement.java
+++ b/org.jacoco.report/src/org/jacoco/report/internal/xml/ReportElement.java
@@ -32,7 +32,7 @@ public class ReportElement extends XMLElement {
private static final String SYSTEM = "report.dtd";
/**
- * Creates a report root element for a XML report.
+ * Creates a report root element for an XML report.
*
* @param name
* value for the name attribute
diff --git a/org.jacoco.report/src/org/jacoco/report/internal/xml/XMLElement.java b/org.jacoco.report/src/org/jacoco/report/internal/xml/XMLElement.java
index 4ecfdfcc05..3e527906fa 100644
--- a/org.jacoco.report/src/org/jacoco/report/internal/xml/XMLElement.java
+++ b/org.jacoco.report/src/org/jacoco/report/internal/xml/XMLElement.java
@@ -20,8 +20,8 @@
import java.io.Writer;
/**
- * Simple API to create well formed XML streams with minimal memory overhead. A
- * {@link XMLElement} instance represents a single element in a XML document.
+ * Simple API to create well formed XML streams with minimal memory overhead. An
+ * {@link XMLElement} instance represents a single element in an XML document.
* {@link XMLElement} can be used directly or might be subclassed for schema
* specific convenience methods.
*/
@@ -60,7 +60,7 @@ private XMLElement(final Writer writer, final String name,
}
/**
- * Creates a root element of a XML document.
+ * Creates a root element of an XML document.
*
* @param name
* element name
@@ -95,7 +95,7 @@ public XMLElement(final String name, final String pubId,
}
/**
- * Creates a new child element within a XML document. May only be called
+ * Creates a new child element within an XML document. May only be called
* before the parent element has been closed.
*
* @param name
From e1340eb7a6d96a5f1fd0dc2337b35bbc94220589 Mon Sep 17 00:00:00 2001
From: Evgeny Mandrikov <138671+Godin@users.noreply.github.com>
Date: Mon, 25 Nov 2024 11:56:14 +0100
Subject: [PATCH 147/255] Add unit test for LocalProbeArrayStrategy (#1792)
---
.../instr/LocalProbeArrayStrategyTest.java | 73 +++++++++++++++++++
1 file changed, 73 insertions(+)
create mode 100644 org.jacoco.core.test/src/org/jacoco/core/internal/instr/LocalProbeArrayStrategyTest.java
diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/instr/LocalProbeArrayStrategyTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/instr/LocalProbeArrayStrategyTest.java
new file mode 100644
index 0000000000..7a4ce363b8
--- /dev/null
+++ b/org.jacoco.core.test/src/org/jacoco/core/internal/instr/LocalProbeArrayStrategyTest.java
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * 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.instr;
+
+import static org.junit.Assert.assertEquals;
+
+import org.jacoco.core.instr.MethodRecorder;
+import org.jacoco.core.runtime.IExecutionDataAccessorGenerator;
+import org.junit.Before;
+import org.junit.Test;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.tree.ClassNode;
+
+/**
+ * Unit tests for {@link LocalProbeArrayStrategy}.
+ */
+public class LocalProbeArrayStrategyTest {
+
+ private LocalProbeArrayStrategy strategy;
+
+ @Before
+ public void setup() {
+ strategy = new LocalProbeArrayStrategy("ClassName", 1L, 2,
+ new IExecutionDataAccessorGenerator() {
+ public int generateDataAccessor(final long classid,
+ final String classname, final int probecount,
+ final MethodVisitor mv) {
+ assertEquals(1L, classid);
+ assertEquals(2, probecount);
+ assertEquals("ClassName", classname);
+ mv.visitMethodInsn(Opcodes.INVOKESTATIC, "Runtime",
+ "getProbes", "()[Z", false);
+ return 42;
+ }
+ });
+ }
+
+ @Test
+ public void should_store_instance() {
+ final MethodRecorder actualMethod = new MethodRecorder();
+ final int maxStack = strategy.storeInstance(actualMethod.getVisitor(),
+ false, 13);
+ assertEquals(42, maxStack);
+
+ final MethodRecorder expectedMethod = new MethodRecorder();
+ final MethodVisitor expected = expectedMethod.getVisitor();
+ expected.visitMethodInsn(Opcodes.INVOKESTATIC, "Runtime", "getProbes",
+ "()[Z", false);
+ expected.visitVarInsn(Opcodes.ASTORE, 13);
+ assertEquals(expectedMethod, actualMethod);
+ }
+
+ @Test
+ public void should_not_add_members() {
+ final ClassNode c = new ClassNode();
+ strategy.addMembers(c, 0);
+
+ assertEquals(0, c.methods.size());
+ assertEquals(0, c.fields.size());
+ }
+
+}
From 667655df85737199a0942943027c80e845a90aae Mon Sep 17 00:00:00 2001
From: Evgeny Mandrikov <138671+Godin@users.noreply.github.com>
Date: Mon, 25 Nov 2024 12:26:41 +0100
Subject: [PATCH 148/255] Fix typo: replace "a HTML" by "an HTML" (#1793)
"an" should be used instead of "a" when the
following word starts with a vowel sound.
---
.../src/org/jacoco/examples/ReportGenerator.java | 2 +-
.../src/org/jacoco/report/internal/html/IHTMLReportContext.java | 2 +-
.../src/org/jacoco/report/internal/html/index/ElementIndex.java | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/org.jacoco.examples/src/org/jacoco/examples/ReportGenerator.java b/org.jacoco.examples/src/org/jacoco/examples/ReportGenerator.java
index 4aa3a0a86f..e63b5b051a 100644
--- a/org.jacoco.examples/src/org/jacoco/examples/ReportGenerator.java
+++ b/org.jacoco.examples/src/org/jacoco/examples/ReportGenerator.java
@@ -25,7 +25,7 @@
import org.jacoco.report.html.HTMLFormatter;
/**
- * This example creates a HTML report for eclipse like projects based on a
+ * This example creates an HTML report for eclipse like projects based on a
* single execution data store called jacoco.exec. The report contains no
* grouping information.
*
diff --git a/org.jacoco.report/src/org/jacoco/report/internal/html/IHTMLReportContext.java b/org.jacoco.report/src/org/jacoco/report/internal/html/IHTMLReportContext.java
index 5ea660791f..8fbd97424b 100644
--- a/org.jacoco.report/src/org/jacoco/report/internal/html/IHTMLReportContext.java
+++ b/org.jacoco.report/src/org/jacoco/report/internal/html/IHTMLReportContext.java
@@ -20,7 +20,7 @@
import org.jacoco.report.internal.html.table.Table;
/**
- * Context and configuration information during creation of a HTML report.
+ * Context and configuration information during creation of an HTML report.
*/
public interface IHTMLReportContext {
diff --git a/org.jacoco.report/src/org/jacoco/report/internal/html/index/ElementIndex.java b/org.jacoco.report/src/org/jacoco/report/internal/html/index/ElementIndex.java
index b72b8cd440..3721204dac 100644
--- a/org.jacoco.report/src/org/jacoco/report/internal/html/index/ElementIndex.java
+++ b/org.jacoco.report/src/org/jacoco/report/internal/html/index/ElementIndex.java
@@ -29,7 +29,7 @@ public class ElementIndex implements IIndexUpdate {
private final Map allClasses = new HashMap();
/**
- * Creates a new empty index for a HTML report.
+ * Creates a new empty index for an HTML report.
*
* @param baseFolder
* base folder where all links are calculated relative to
From e4d600ab24ae2f3dea8231f15e13d71876e77bda Mon Sep 17 00:00:00 2001
From: Evgeny Mandrikov <138671+Godin@users.noreply.github.com>
Date: Mon, 25 Nov 2024 14:36:17 +0100
Subject: [PATCH 149/255] Fix typo: replace "a agent" by "an agent" (#1795)
"an" should be used instead of "a" when the
following word starts with a vowel sound.
---
org.jacoco.agent.rt/src/org/jacoco/agent/rt/internal/Agent.java | 2 +-
org.jacoco.core/src/org/jacoco/core/runtime/AgentOptions.java | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/org.jacoco.agent.rt/src/org/jacoco/agent/rt/internal/Agent.java b/org.jacoco.agent.rt/src/org/jacoco/agent/rt/internal/Agent.java
index 4d5d82179c..0568acb1b2 100644
--- a/org.jacoco.agent.rt/src/org/jacoco/agent/rt/internal/Agent.java
+++ b/org.jacoco.agent.rt/src/org/jacoco/agent/rt/internal/Agent.java
@@ -64,7 +64,7 @@ public void run() {
}
/**
- * Returns a global instance which is already started. If a agent has not
+ * Returns a global instance which is already started. If an agent has not
* been initialized before this method will fail.
*
* @return global instance
diff --git a/org.jacoco.core/src/org/jacoco/core/runtime/AgentOptions.java b/org.jacoco.core/src/org/jacoco/core/runtime/AgentOptions.java
index d3b29d3c9d..e9e862db98 100644
--- a/org.jacoco.core/src/org/jacoco/core/runtime/AgentOptions.java
+++ b/org.jacoco.core/src/org/jacoco/core/runtime/AgentOptions.java
@@ -610,7 +610,7 @@ public String getQuotedVMArgument(final File agentJarFile) {
/**
* Generate required quotes JVM argument based on current configuration and
- * prepends it to the given argument command line. If a agent with the same
+ * prepends it to the given argument command line. If an agent with the same
* JAR file is already specified this parameter is removed from the existing
* command line.
*
From 081d054a06e944625546d50a463fc72e1bba67d1 Mon Sep 17 00:00:00 2001
From: Evgeny Mandrikov <138671+Godin@users.noreply.github.com>
Date: Mon, 25 Nov 2024 21:13:45 +0100
Subject: [PATCH 150/255] Add unit test for ClassFieldProbeArrayStrategy
(#1796)
---
.../ClassFieldProbeArrayStrategyTest.java | 113 ++++++++++++++++++
1 file changed, 113 insertions(+)
create mode 100644 org.jacoco.core.test/src/org/jacoco/core/internal/instr/ClassFieldProbeArrayStrategyTest.java
diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/instr/ClassFieldProbeArrayStrategyTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/instr/ClassFieldProbeArrayStrategyTest.java
new file mode 100644
index 0000000000..d96eccc05f
--- /dev/null
+++ b/org.jacoco.core.test/src/org/jacoco/core/internal/instr/ClassFieldProbeArrayStrategyTest.java
@@ -0,0 +1,113 @@
+/*******************************************************************************
+ * 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.instr;
+
+import static org.junit.Assert.assertEquals;
+
+import org.jacoco.core.instr.MethodRecorder;
+import org.jacoco.core.runtime.IExecutionDataAccessorGenerator;
+import org.junit.Test;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.tree.ClassNode;
+import org.objectweb.asm.tree.FieldNode;
+import org.objectweb.asm.tree.MethodNode;
+
+/**
+ * Unit test for {@link ClassFieldProbeArrayStrategy}.
+ */
+public class ClassFieldProbeArrayStrategyTest {
+
+ private ClassFieldProbeArrayStrategy create(final boolean withFrames) {
+ return new ClassFieldProbeArrayStrategy("ClassName", 1L, withFrames,
+ new IExecutionDataAccessorGenerator() {
+ public int generateDataAccessor(final long classid,
+ final String classname, final int probecount,
+ final MethodVisitor mv) {
+ assertEquals(1L, classid);
+ assertEquals("ClassName", classname);
+ assertEquals(2, probecount);
+ mv.visitMethodInsn(Opcodes.INVOKESTATIC, "Runtime",
+ "getProbes", "()[Z", false);
+ return 0;
+ }
+ });
+ }
+
+ @Test
+ public void should_add_field_and_init_method() {
+ for (final boolean withFrames : new boolean[] { true, false }) {
+ final ClassFieldProbeArrayStrategy strategy = create(withFrames);
+
+ final ClassNode c = new ClassNode();
+ strategy.addMembers(c, 2);
+
+ assertEquals(1, c.fields.size());
+ final FieldNode f = c.fields.get(0);
+ assertEquals(
+ Opcodes.ACC_SYNTHETIC | Opcodes.ACC_PRIVATE
+ | Opcodes.ACC_STATIC | Opcodes.ACC_TRANSIENT,
+ f.access);
+ assertEquals("$jacocoData", f.name);
+ assertEquals("[Z", f.desc);
+
+ assertEquals(1, c.methods.size());
+ final MethodNode m = c.methods.get(0);
+ assertEquals(Opcodes.ACC_SYNTHETIC | Opcodes.ACC_PRIVATE
+ | Opcodes.ACC_STATIC, m.access);
+ assertEquals("$jacocoInit", m.name);
+ assertEquals("()[Z", m.desc);
+ final MethodRecorder expectedMethod = new MethodRecorder();
+ final MethodVisitor expected = expectedMethod.getVisitor();
+ expected.visitFieldInsn(Opcodes.GETSTATIC, "ClassName",
+ "$jacocoData", "[Z");
+ expected.visitInsn(Opcodes.DUP);
+ final Label label = new Label();
+ expected.visitJumpInsn(Opcodes.IFNONNULL, label);
+ expected.visitInsn(Opcodes.POP);
+ expected.visitMethodInsn(Opcodes.INVOKESTATIC, "Runtime",
+ "getProbes", "()[Z", false);
+ expected.visitInsn(Opcodes.DUP);
+ expected.visitFieldInsn(Opcodes.PUTSTATIC, "ClassName",
+ "$jacocoData", "[Z");
+ if (withFrames) {
+ expected.visitFrame(Opcodes.F_FULL, 0, new Object[] {}, 1,
+ new Object[] { "[Z" });
+ }
+ expected.visitLabel(label);
+ expected.visitInsn(Opcodes.ARETURN);
+ final MethodRecorder actualMethod = new MethodRecorder();
+ m.instructions.accept(actualMethod.getVisitor());
+ assertEquals(expectedMethod, actualMethod);
+ }
+ }
+
+ @Test
+ public void should_store_instance() {
+ final ClassFieldProbeArrayStrategy strategy = create(true);
+
+ final MethodRecorder actualMethod = new MethodRecorder();
+ final int maxStack = strategy.storeInstance(actualMethod.getVisitor(),
+ false, 13);
+ assertEquals(1, maxStack);
+
+ final MethodRecorder expectedMethod = new MethodRecorder();
+ final MethodVisitor expected = expectedMethod.getVisitor();
+ expected.visitMethodInsn(Opcodes.INVOKESTATIC, "ClassName",
+ "$jacocoInit", "()[Z", false);
+ expected.visitVarInsn(Opcodes.ASTORE, 13);
+ assertEquals(expectedMethod, actualMethod);
+ }
+
+}
From b71736944cf29cfc0a51292a3f6885ea3a377cd4 Mon Sep 17 00:00:00 2001
From: Evgeny Mandrikov <138671+Godin@users.noreply.github.com>
Date: Mon, 25 Nov 2024 23:56:20 +0100
Subject: [PATCH 151/255] Fix javadoc doclint warnings "no comment" (#1798)
---
.../src/org/jacoco/core/analysis/CounterComparator.java | 7 +++++++
.../src/org/jacoco/core/analysis/NodeComparator.java | 6 ++++++
.../core/data/IncompatibleExecDataVersionException.java | 3 +++
.../jacoco/core/internal/analysis/ClassCoverageImpl.java | 7 +++++++
.../internal/analysis/filter/KotlinGeneratedFilter.java | 9 +++++++++
.../jacoco/core/internal/analysis/filter/KotlinSMAP.java | 4 ++++
6 files changed, 36 insertions(+)
diff --git a/org.jacoco.core/src/org/jacoco/core/analysis/CounterComparator.java b/org.jacoco.core/src/org/jacoco/core/analysis/CounterComparator.java
index 2845a6a381..fcf143e739 100644
--- a/org.jacoco.core/src/org/jacoco/core/analysis/CounterComparator.java
+++ b/org.jacoco.core/src/org/jacoco/core/analysis/CounterComparator.java
@@ -56,7 +56,14 @@ public class CounterComparator implements Comparator, Serializable {
public static final CounterComparator MISSEDRATIO = new CounterComparator(
CounterValue.MISSEDRATIO);
+ /**
+ * Counter value to sort on.
+ */
private final CounterValue value;
+
+ /**
+ * Specifies whether values are sorted in reverse order or not.
+ */
private final boolean reverse;
private CounterComparator(final CounterValue value) {
diff --git a/org.jacoco.core/src/org/jacoco/core/analysis/NodeComparator.java b/org.jacoco.core/src/org/jacoco/core/analysis/NodeComparator.java
index e5ad65bf72..1bbbbff9ca 100644
--- a/org.jacoco.core/src/org/jacoco/core/analysis/NodeComparator.java
+++ b/org.jacoco.core/src/org/jacoco/core/analysis/NodeComparator.java
@@ -31,8 +31,14 @@ public class NodeComparator implements Comparator, Serializable {
private static final long serialVersionUID = 8550521643608826519L;
+ /**
+ * Comparator to compare {@link ICounter} objects.
+ */
private final Comparator counterComparator;
+ /**
+ * Counter entity to sort on.
+ */
private final CounterEntity entity;
NodeComparator(final Comparator counterComparator,
diff --git a/org.jacoco.core/src/org/jacoco/core/data/IncompatibleExecDataVersionException.java b/org.jacoco.core/src/org/jacoco/core/data/IncompatibleExecDataVersionException.java
index 7cb998043b..32d93c4840 100644
--- a/org.jacoco.core/src/org/jacoco/core/data/IncompatibleExecDataVersionException.java
+++ b/org.jacoco.core/src/org/jacoco/core/data/IncompatibleExecDataVersionException.java
@@ -21,6 +21,9 @@ public class IncompatibleExecDataVersionException extends IOException {
private static final long serialVersionUID = 1L;
+ /**
+ * Actual version found in the execution data.
+ */
private final int actualVersion;
/**
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/ClassCoverageImpl.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/ClassCoverageImpl.java
index f4dc7e8870..b315d25703 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/ClassCoverageImpl.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/ClassCoverageImpl.java
@@ -119,6 +119,13 @@ public Collection getFragments() {
return fragments;
}
+ /**
+ * Stores fragments that contain coverage information about other nodes
+ * collected during the creation of this node.
+ *
+ * @param fragments
+ * fragments to store
+ */
public void setFragments(final Collection fragments) {
this.fragments = fragments;
}
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinGeneratedFilter.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinGeneratedFilter.java
index 2bc32b1a21..535bd6a082 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinGeneratedFilter.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinGeneratedFilter.java
@@ -24,6 +24,15 @@ public class KotlinGeneratedFilter implements IFilter {
static final String KOTLIN_METADATA_DESC = "Lkotlin/Metadata;";
+ /**
+ * Checks whether the class corresponding to the given context has
+ * kotlin/Metadata annotation.
+ *
+ * @param context
+ * context information
+ * @return true if the class corresponding to the given context
+ * has kotlin/Metadata annotation
+ */
public static boolean isKotlinClass(final IFilterContext context) {
return context.getClassAnnotations().contains(KOTLIN_METADATA_DESC);
}
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinSMAP.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinSMAP.java
index aa7a8d7b12..a3de7d9a77 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinSMAP.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinSMAP.java
@@ -26,6 +26,10 @@
*/
public final class KotlinSMAP {
+ /**
+ * Parsed representation of a single LineSection from SourceDebugExtension
+ * attribute.
+ */
public static final class Mapping {
private final String inputClassName;
private final int inputStartLine;
From 83dba7880aef2ebffcec91012a3544cab6877ad8 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 27 Nov 2024 15:38:01 +0000
Subject: [PATCH 152/255] Upgrade Kotlin to 2.1.0 (#1802)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Evgeny Mandrikov
---
org.jacoco.core.test.validation.kotlin/pom.xml | 2 +-
org.jacoco.core.test.validation/pom.xml | 7 +++----
2 files changed, 4 insertions(+), 5 deletions(-)
diff --git a/org.jacoco.core.test.validation.kotlin/pom.xml b/org.jacoco.core.test.validation.kotlin/pom.xml
index 24e23fa3c9..3708dc868d 100644
--- a/org.jacoco.core.test.validation.kotlin/pom.xml
+++ b/org.jacoco.core.test.validation.kotlin/pom.xml
@@ -25,7 +25,7 @@
JaCoCo :: Test :: Core :: Validation Kotlin
- 2.0.21
+ 2.1.0
diff --git a/org.jacoco.core.test.validation/pom.xml b/org.jacoco.core.test.validation/pom.xml
index b18a0e1690..2d6edc5f12 100644
--- a/org.jacoco.core.test.validation/pom.xml
+++ b/org.jacoco.core.test.validation/pom.xml
@@ -504,8 +504,7 @@
-
- 22
+ 2316
@@ -533,8 +532,8 @@
-
- 22
+
+ 2316
From 46f0a6dfe349a3a220187735bcf0e3dd27018a28 Mon Sep 17 00:00:00 2001
From: Evgeny Mandrikov <138671+Godin@users.noreply.github.com>
Date: Thu, 28 Nov 2024 00:24:19 +0100
Subject: [PATCH 153/255] Upgrade maven-invoker-plugin to 3.8.1 (#1457)
---
.github/dependabot.yml | 1 -
jacoco-maven-plugin.test/pom.xml | 12 +++++++++---
org.jacoco.build/pom.xml | 2 +-
org.jacoco.examples.test/pom.xml | 6 ++++++
4 files changed, 16 insertions(+), 5 deletions(-)
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index d5d2b4272d..0a6c79fce2 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -22,7 +22,6 @@ updates:
ignore:
# It is known that upgrade from current version requires additional changes:
- dependency-name: "org.apache.maven.plugins:maven-plugin-plugin"
- - dependency-name: "org.apache.maven.plugins:maven-invoker-plugin"
# Because of
# https://github.com/apache/maven-compiler-plugin/blob/maven-compiler-plugin-3.13.0/pom.xml#L71
# https://github.com/codehaus-plexus/plexus-compiler/blob/plexus-compiler-2.15.0/plexus-compilers/plexus-compiler-javac/src/main/java/org/codehaus/plexus/compiler/javac/JavacCompiler.java#L149-L163
diff --git a/jacoco-maven-plugin.test/pom.xml b/jacoco-maven-plugin.test/pom.xml
index ffca23e99c..81e8e2468e 100644
--- a/jacoco-maven-plugin.test/pom.xml
+++ b/jacoco-maven-plugin.test/pom.xml
@@ -32,6 +32,15 @@
org.jacoco.maven.*
+
+
+ ${project.groupId}
+ jacoco-maven-plugin
+ ${project.version}
+ maven-plugin
+
+
+
@@ -52,9 +61,6 @@
installit/settings.xml
-
- org.jacoco:org.jacoco.agent:${project.version}:jar:runtime
- ecj
diff --git a/org.jacoco.core.test.validation/pom.xml b/org.jacoco.core.test.validation/pom.xml
index 2d6edc5f12..10583e0524 100644
--- a/org.jacoco.core.test.validation/pom.xml
+++ b/org.jacoco.core.test.validation/pom.xml
@@ -554,6 +554,39 @@
+
+ java25-bytecode
+
+
+ bytecode.version
+ 25
+
+
+
+
+ 23
+
+ 16
+
+ 25
+ 25
+
+
+
+ ../org.jacoco.core.test.validation.java7
+ ../org.jacoco.core.test.validation.java8
+ ../org.jacoco.core.test.validation.java14
+ ../org.jacoco.core.test.validation.java16
+ ../org.jacoco.core.test.validation.java21
+
+ ../org.jacoco.core.test.validation.scala
+
+
+
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 b4ac0c865b..69d7af7763 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
@@ -132,13 +132,13 @@ private static byte[] createClass(final int version) {
*/
@Test
public void analyzeClass_should_throw_exception_for_unsupported_class_file_version() {
- final byte[] bytes = createClass(Opcodes.V23 + 2);
+ final byte[] bytes = createClass(Opcodes.V24 + 2);
try {
analyzer.analyzeClass(bytes, "UnsupportedVersion");
fail("exception expected");
} catch (IOException e) {
assertExceptionMessage("UnsupportedVersion", e);
- assertEquals("Unsupported class file major version 69",
+ assertEquals("Unsupported class file major version 70",
e.getCause().getMessage());
}
}
@@ -218,14 +218,14 @@ public void testAnalyzeClass_BrokenStream() throws IOException {
*/
@Test
public void analyzeAll_should_throw_exception_for_unsupported_class_file_version() {
- final byte[] bytes = createClass(Opcodes.V23 + 2);
+ final byte[] bytes = createClass(Opcodes.V24 + 2);
try {
analyzer.analyzeAll(new ByteArrayInputStream(bytes),
"UnsupportedVersion");
fail("exception expected");
} catch (IOException e) {
assertExceptionMessage("UnsupportedVersion", e);
- assertEquals("Unsupported class file major version 69",
+ assertEquals("Unsupported class file major version 70",
e.getCause().getMessage());
}
}
diff --git a/org.jacoco.core.test/src/org/jacoco/core/instr/ClassFileVersionsTest.java b/org.jacoco.core.test/src/org/jacoco/core/instr/ClassFileVersionsTest.java
index e3b31b2d9d..2d8172a779 100644
--- a/org.jacoco.core.test/src/org/jacoco/core/instr/ClassFileVersionsTest.java
+++ b/org.jacoco.core.test/src/org/jacoco/core/instr/ClassFileVersionsTest.java
@@ -31,6 +31,9 @@
import static org.objectweb.asm.Opcodes.V14;
import static org.objectweb.asm.Opcodes.V15;
import static org.objectweb.asm.Opcodes.V16;
+import static org.objectweb.asm.Opcodes.V17;
+import static org.objectweb.asm.Opcodes.V18;
+import static org.objectweb.asm.Opcodes.V19;
import static org.objectweb.asm.Opcodes.V1_1;
import static org.objectweb.asm.Opcodes.V1_2;
import static org.objectweb.asm.Opcodes.V1_3;
@@ -39,6 +42,11 @@
import static org.objectweb.asm.Opcodes.V1_6;
import static org.objectweb.asm.Opcodes.V1_7;
import static org.objectweb.asm.Opcodes.V1_8;
+import static org.objectweb.asm.Opcodes.V20;
+import static org.objectweb.asm.Opcodes.V21;
+import static org.objectweb.asm.Opcodes.V22;
+import static org.objectweb.asm.Opcodes.V23;
+import static org.objectweb.asm.Opcodes.V24;
import static org.objectweb.asm.Opcodes.V9;
import java.io.IOException;
@@ -139,6 +147,51 @@ public void test_16() throws IOException {
testVersion(V16, true);
}
+ @Test
+ public void test_17() throws IOException {
+ testVersion(V17, true);
+ }
+
+ @Test
+ public void test_18() throws IOException {
+ testVersion(V18, true);
+ }
+
+ @Test
+ public void test_19() throws IOException {
+ testVersion(V19, true);
+ }
+
+ @Test
+ public void test_20() throws IOException {
+ testVersion(V20, true);
+ }
+
+ @Test
+ public void test_21() throws IOException {
+ testVersion(V21, true);
+ }
+
+ @Test
+ public void test_22() throws IOException {
+ testVersion(V22, true);
+ }
+
+ @Test
+ public void test_23() throws IOException {
+ testVersion(V23, true);
+ }
+
+ @Test
+ public void test_24() throws IOException {
+ testVersion(V24, true);
+ }
+
+ @Test
+ public void test_25() throws IOException {
+ testVersion(V24 + 1, true);
+ }
+
private void testVersion(int version, boolean frames) throws IOException {
final byte[] original = createClass(version, frames);
diff --git a/org.jacoco.core.test/src/org/jacoco/core/instr/InstrumenterTest.java b/org.jacoco.core.test/src/org/jacoco/core/instr/InstrumenterTest.java
index 04a0ba972f..2c9d596e70 100644
--- a/org.jacoco.core.test/src/org/jacoco/core/instr/InstrumenterTest.java
+++ b/org.jacoco.core.test/src/org/jacoco/core/instr/InstrumenterTest.java
@@ -123,13 +123,13 @@ private static byte[] createClass(final int version) {
*/
@Test
public void instrument_should_throw_exception_for_unsupported_class_file_version() {
- final byte[] bytes = createClass(Opcodes.V23 + 2);
+ final byte[] bytes = createClass(Opcodes.V24 + 2);
try {
instrumenter.instrument(bytes, "UnsupportedVersion");
fail("exception expected");
} catch (final IOException e) {
assertExceptionMessage("UnsupportedVersion", e);
- assertEquals("Unsupported class file major version 69",
+ assertEquals("Unsupported class file major version 70",
e.getCause().getMessage());
}
}
@@ -221,14 +221,14 @@ public void testSerialization() throws Exception {
*/
@Test
public void instrumentAll_should_throw_exception_for_unsupported_class_file_version() {
- final byte[] bytes = createClass(Opcodes.V23 + 2);
+ final byte[] bytes = createClass(Opcodes.V24 + 2);
try {
instrumenter.instrumentAll(new ByteArrayInputStream(bytes),
new ByteArrayOutputStream(), "UnsupportedVersion");
fail("exception expected");
} catch (final IOException e) {
assertExceptionMessage("UnsupportedVersion", e);
- assertEquals("Unsupported class file major version 69",
+ assertEquals("Unsupported class file major version 70",
e.getCause().getMessage());
}
}
diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/instr/InstrSupportTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/instr/InstrSupportTest.java
index 1ee2e7d74a..fa96a54caf 100644
--- a/org.jacoco.core.test/src/org/jacoco/core/internal/instr/InstrSupportTest.java
+++ b/org.jacoco.core.test/src/org/jacoco/core/internal/instr/InstrSupportTest.java
@@ -43,8 +43,8 @@ public void setup() {
}
@Test
- public void classReaderFor_should_read_java_24_class() {
- final byte[] bytes = createJava24Class();
+ public void classReaderFor_should_read_java_25_class() {
+ final byte[] bytes = createJava25Class();
final ClassReader classReader = InstrSupport.classReaderFor(bytes);
@@ -53,16 +53,16 @@ public void classReaderFor_should_read_java_24_class() {
public void visit(final int version, final int access,
final String name, final String signature,
final String superName, final String[] interfaces) {
- assertEquals(Opcodes.V23 + 1, version);
+ assertEquals(Opcodes.V24 + 1, version);
}
}, 0);
- assertArrayEquals(createJava24Class(), bytes);
+ assertArrayEquals(createJava25Class(), bytes);
}
- private static byte[] createJava24Class() {
+ private static byte[] createJava25Class() {
final ClassWriter cw = new ClassWriter(0);
- cw.visit(Opcodes.V23 + 1, 0, "Foo", null, "java/lang/Object", null);
+ cw.visit(Opcodes.V24 + 1, 0, "Foo", null, "java/lang/Object", null);
cw.visitEnd();
return cw.toByteArray();
}
@@ -135,7 +135,8 @@ public void needFrames_should_return_true_for_versions_greater_than_or_equal_to_
assertTrue(InstrSupport.needsFrames(Opcodes.V21));
assertTrue(InstrSupport.needsFrames(Opcodes.V22));
assertTrue(InstrSupport.needsFrames(Opcodes.V23));
- assertTrue(InstrSupport.needsFrames(Opcodes.V23 + 1));
+ assertTrue(InstrSupport.needsFrames(Opcodes.V24));
+ assertTrue(InstrSupport.needsFrames(Opcodes.V24 + 1));
assertTrue(InstrSupport.needsFrames(0x0100));
}
diff --git a/org.jacoco.core.test/src/org/jacoco/core/test/validation/ValidationTestBase.java b/org.jacoco.core.test/src/org/jacoco/core/test/validation/ValidationTestBase.java
index e3417a3d7a..bd54e2d680 100644
--- a/org.jacoco.core.test/src/org/jacoco/core/test/validation/ValidationTestBase.java
+++ b/org.jacoco.core.test/src/org/jacoco/core/test/validation/ValidationTestBase.java
@@ -32,6 +32,7 @@
import org.jacoco.core.data.ExecutionDataStore;
import org.jacoco.core.data.SessionInfo;
import org.jacoco.core.internal.analysis.CounterImpl;
+import org.jacoco.core.internal.instr.InstrSupport;
import org.jacoco.core.test.InstrumentingLoader;
import org.jacoco.core.test.TargetLoader;
import org.jacoco.core.test.validation.Source.Line;
@@ -44,7 +45,6 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runners.model.MultipleFailureException;
-import org.objectweb.asm.ClassReader;
import org.objectweb.asm.util.ASMifier;
import org.objectweb.asm.util.Textifier;
import org.objectweb.asm.util.TraceClassVisitor;
@@ -124,9 +124,10 @@ private void saveBytecodeRepresentations(final byte[] classBytes,
new File(outputDir, fileName + ".txt"));
final PrintWriter asmWriter = new PrintWriter(
new File(outputDir, fileName + ".java"));
- new ClassReader(classBytes).accept(new TraceClassVisitor(
- new TraceClassVisitor(null, new Textifier(), textWriter),
- new ASMifier(), asmWriter), 0);
+ InstrSupport.classReaderFor(classBytes)
+ .accept(new TraceClassVisitor(new TraceClassVisitor(null,
+ new Textifier(), textWriter), new ASMifier(),
+ asmWriter), 0);
textWriter.close();
asmWriter.close();
}
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/instr/InstrSupport.java b/org.jacoco.core/src/org/jacoco/core/internal/instr/InstrSupport.java
index c52810e663..dc4df54e01 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/instr/InstrSupport.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/instr/InstrSupport.java
@@ -273,9 +273,9 @@ public static void push(final MethodVisitor mv, final int value) {
*/
public static ClassReader classReaderFor(final byte[] b) {
final int originalVersion = getMajorVersion(b);
- if (originalVersion == Opcodes.V23 + 1) {
+ if (originalVersion == Opcodes.V24 + 1) {
// temporarily downgrade version to bypass check in ASM
- setMajorVersion(Opcodes.V23, b);
+ setMajorVersion(Opcodes.V24, b);
}
final ClassReader classReader = new ClassReader(b);
setMajorVersion(originalVersion, b);
diff --git a/org.jacoco.doc/docroot/doc/changes.html b/org.jacoco.doc/docroot/doc/changes.html
index 2a5caaa836..c4ce95b553 100644
--- a/org.jacoco.doc/docroot/doc/changes.html
+++ b/org.jacoco.doc/docroot/doc/changes.html
@@ -34,6 +34,8 @@
Does JaCoCo have a plug-in for [Eclipse|Netbeans|Whatever...]?
What Java versions are supported by JaCoCo?
- JaCoCo supports Java class files from version 1.0 to 23. However the minimum
- JRE version required by the JaCoCo runtime (e.g. the agent) and the JaCoCo
- tools is 1.5. Also note that class files under test from version 1.6 and above
- have to contain valid stackmap frames.
+ JaCoCo officially supports Java class files from version 1.0 to 23. Also
+ experimental support for class files of versions 24 and 25 is provided.
+ However the minimum JRE version required by the JaCoCo runtime
+ (e.g. the agent) and the JaCoCo tools is 1.5. Also note that class files under
+ test from version 1.6 and above have to contain valid stackmap frames.
Why do I get the error "Can't add different class with same name"?
From 5a6a5c4719aa009c1f2117f4ccc04d19d6d812fc Mon Sep 17 00:00:00 2001
From: Evgeny Mandrikov <138671+Godin@users.noreply.github.com>
Date: Mon, 9 Dec 2024 15:19:20 +0100
Subject: [PATCH 159/255] IFilter implementations should be package-private and
final (#1806)
---
.../core/internal/analysis/ClassAnalyzer.java | 4 +---
.../filter/AnnotationGeneratedFilter.java | 2 +-
.../filter/EnumEmptyConstructorFilter.java | 2 +-
.../internal/analysis/filter/EnumFilter.java | 2 +-
.../core/internal/analysis/filter/Filters.java | 14 ++++++++++++++
.../internal/analysis/filter/FinallyFilter.java | 2 +-
.../analysis/filter/KotlinComposeFilter.java | 2 +-
.../analysis/filter/KotlinCoroutineFilter.java | 4 ++--
.../filter/KotlinDefaultArgumentsFilter.java | 4 ++--
.../filter/KotlinDefaultMethodsFilter.java | 2 +-
.../analysis/filter/KotlinEnumFilter.java | 2 +-
.../analysis/filter/KotlinGeneratedFilter.java | 17 ++---------------
.../filter/KotlinInlineClassFilter.java | 2 +-
.../analysis/filter/KotlinInlineFilter.java | 4 ++--
.../analysis/filter/KotlinLateinitFilter.java | 2 +-
.../filter/KotlinNotNullOperatorFilter.java | 2 +-
.../filter/KotlinUnsafeCastOperatorFilter.java | 4 ++--
.../analysis/filter/KotlinWhenFilter.java | 2 +-
.../analysis/filter/KotlinWhenStringFilter.java | 2 +-
.../PrivateEmptyNoArgConstructorFilter.java | 2 +-
.../internal/analysis/filter/RecordsFilter.java | 2 +-
.../analysis/filter/StringSwitchFilter.java | 2 +-
.../filter/StringSwitchJavacFilter.java | 2 +-
.../analysis/filter/SynchronizedFilter.java | 2 +-
.../analysis/filter/SyntheticFilter.java | 4 ++--
.../filter/TryWithResourcesEcjFilter.java | 2 +-
.../filter/TryWithResourcesJavac11Filter.java | 2 +-
.../filter/TryWithResourcesJavacFilter.java | 2 +-
28 files changed, 47 insertions(+), 48 deletions(-)
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/ClassAnalyzer.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/ClassAnalyzer.java
index d2268c5507..fa49d5385a 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/ClassAnalyzer.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/ClassAnalyzer.java
@@ -21,7 +21,6 @@
import org.jacoco.core.internal.analysis.filter.Filters;
import org.jacoco.core.internal.analysis.filter.IFilter;
import org.jacoco.core.internal.analysis.filter.IFilterContext;
-import org.jacoco.core.internal.analysis.filter.KotlinGeneratedFilter;
import org.jacoco.core.internal.analysis.filter.KotlinSMAP;
import org.jacoco.core.internal.flow.ClassProbesVisitor;
import org.jacoco.core.internal.flow.MethodProbesVisitor;
@@ -144,8 +143,7 @@ private void addMethodCoverage(final String name, final String desc,
private void calculateFragments(
final Map instructions) {
- if (sourceDebugExtension == null
- || !KotlinGeneratedFilter.isKotlinClass(this)) {
+ if (sourceDebugExtension == null || !Filters.isKotlinClass(this)) {
return;
}
if (smap == null) {
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/AnnotationGeneratedFilter.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/AnnotationGeneratedFilter.java
index cd7818d85c..9e80e75b98 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/AnnotationGeneratedFilter.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/AnnotationGeneratedFilter.java
@@ -23,7 +23,7 @@
* {@link java.lang.annotation.RetentionPolicy#CLASS invisible} annotation whose
* simple name contains Generated.
*/
-public final class AnnotationGeneratedFilter implements IFilter {
+final class AnnotationGeneratedFilter implements IFilter {
public void filter(final MethodNode methodNode,
final IFilterContext context, final IFilterOutput output) {
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/EnumEmptyConstructorFilter.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/EnumEmptyConstructorFilter.java
index bfc2d80988..e63e17c263 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/EnumEmptyConstructorFilter.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/EnumEmptyConstructorFilter.java
@@ -29,7 +29,7 @@
* constructors} - empty constructor in enums without additional parameters
* should be filtered out even if it is not implicit.
*/
-public final class EnumEmptyConstructorFilter implements IFilter {
+final class EnumEmptyConstructorFilter implements IFilter {
private static final String CONSTRUCTOR_NAME = "";
private static final String CONSTRUCTOR_DESC = "(Ljava/lang/String;I)V";
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/EnumFilter.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/EnumFilter.java
index 22bc1f0100..7ea7b99fcd 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/EnumFilter.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/EnumFilter.java
@@ -18,7 +18,7 @@
* Filters methods values and valueOf that compiler
* creates for enums.
*/
-public final class EnumFilter implements IFilter {
+final class EnumFilter implements IFilter {
public void filter(final MethodNode methodNode,
final IFilterContext context, final IFilterOutput output) {
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 de1a1f3612..11b748caf5 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
@@ -65,4 +65,18 @@ public void filter(final MethodNode methodNode,
}
}
+ /**
+ * Checks whether the class corresponding to the given context has
+ * kotlin/Metadata annotation.
+ *
+ * @param context
+ * context information
+ * @return true if the class corresponding to the given context
+ * has kotlin/Metadata annotation
+ */
+ public static boolean isKotlinClass(final IFilterContext context) {
+ return context.getClassAnnotations()
+ .contains(KotlinGeneratedFilter.KOTLIN_METADATA_DESC);
+ }
+
}
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/FinallyFilter.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/FinallyFilter.java
index a421fc24b0..bc06d1d600 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/FinallyFilter.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/FinallyFilter.java
@@ -66,7 +66,7 @@
* at once, because execution can branch from one region to another (like it is
* in given example due to "if" statement).
*/
-public final class FinallyFilter implements IFilter {
+final class FinallyFilter implements IFilter {
public void filter(final MethodNode methodNode,
final IFilterContext context, final IFilterOutput output) {
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinComposeFilter.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinComposeFilter.java
index 48a1fbd565..62a53d9235 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinComposeFilter.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinComposeFilter.java
@@ -26,7 +26,7 @@ final class KotlinComposeFilter implements IFilter {
public void filter(final MethodNode methodNode,
final IFilterContext context, final IFilterOutput output) {
- if (!KotlinGeneratedFilter.isKotlinClass(context)) {
+ if (!Filters.isKotlinClass(context)) {
return;
}
if (!isComposable(methodNode)) {
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinCoroutineFilter.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinCoroutineFilter.java
index 64a2016175..c83c28b8cc 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinCoroutineFilter.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinCoroutineFilter.java
@@ -26,12 +26,12 @@
/**
* Filters branches that Kotlin compiler generates for coroutines.
*/
-public final class KotlinCoroutineFilter implements IFilter {
+final class KotlinCoroutineFilter implements IFilter {
public void filter(final MethodNode methodNode,
final IFilterContext context, final IFilterOutput output) {
- if (!KotlinGeneratedFilter.isKotlinClass(context)) {
+ if (!Filters.isKotlinClass(context)) {
return;
}
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinDefaultArgumentsFilter.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinDefaultArgumentsFilter.java
index 978dd29750..61643f138d 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinDefaultArgumentsFilter.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinDefaultArgumentsFilter.java
@@ -50,7 +50,7 @@
*
* This filter marks IFEQ instructions as ignored.
*/
-public final class KotlinDefaultArgumentsFilter implements IFilter {
+final class KotlinDefaultArgumentsFilter implements IFilter {
private static boolean isDefaultArgumentsMethod(
final MethodNode methodNode) {
@@ -76,7 +76,7 @@ public void filter(final MethodNode methodNode,
if ((methodNode.access & Opcodes.ACC_SYNTHETIC) == 0) {
return;
}
- if (!KotlinGeneratedFilter.isKotlinClass(context)) {
+ if (!Filters.isKotlinClass(context)) {
return;
}
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinDefaultMethodsFilter.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinDefaultMethodsFilter.java
index 4ae6b1e93e..d79952216b 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinDefaultMethodsFilter.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinDefaultMethodsFilter.java
@@ -24,7 +24,7 @@ final class KotlinDefaultMethodsFilter implements IFilter {
public void filter(final MethodNode methodNode,
final IFilterContext context, final IFilterOutput output) {
- if (!KotlinGeneratedFilter.isKotlinClass(context)) {
+ if (!Filters.isKotlinClass(context)) {
return;
}
new Matcher().match(methodNode, output);
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinEnumFilter.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinEnumFilter.java
index 5d9891d3f9..43737cd6f0 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinEnumFilter.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinEnumFilter.java
@@ -25,7 +25,7 @@ public void filter(final MethodNode methodNode,
if (!"java/lang/Enum".equals(context.getSuperClassName())) {
return;
}
- if (!KotlinGeneratedFilter.isKotlinClass(context)) {
+ if (!Filters.isKotlinClass(context)) {
return;
}
if (!"getEntries".equals(methodNode.name)) {
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinGeneratedFilter.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinGeneratedFilter.java
index 535bd6a082..7e7b67e0b6 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinGeneratedFilter.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinGeneratedFilter.java
@@ -20,23 +20,10 @@
* identified by the @kotlin.Metadata annotations. In such classes
* generated methods do not have line numbers.
*/
-public class KotlinGeneratedFilter implements IFilter {
+final class KotlinGeneratedFilter implements IFilter {
static final String KOTLIN_METADATA_DESC = "Lkotlin/Metadata;";
- /**
- * Checks whether the class corresponding to the given context has
- * kotlin/Metadata annotation.
- *
- * @param context
- * context information
- * @return true if the class corresponding to the given context
- * has kotlin/Metadata annotation
- */
- public static boolean isKotlinClass(final IFilterContext context) {
- return context.getClassAnnotations().contains(KOTLIN_METADATA_DESC);
- }
-
public void filter(final MethodNode methodNode,
final IFilterContext context, final IFilterOutput output) {
@@ -46,7 +33,7 @@ public void filter(final MethodNode methodNode,
return;
}
- if (!isKotlinClass(context)) {
+ if (!Filters.isKotlinClass(context)) {
return;
}
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
index 0279e04786..41ad6d4e19 100644
--- 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
@@ -73,7 +73,7 @@ final class KotlinInlineClassFilter implements IFilter {
public void filter(final MethodNode methodNode,
final IFilterContext context, final IFilterOutput output) {
- if (!KotlinGeneratedFilter.isKotlinClass(context)) {
+ if (!Filters.isKotlinClass(context)) {
return;
}
if (!context.getClassAnnotations().contains("Lkotlin/jvm/JvmInline;")) {
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinInlineFilter.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinInlineFilter.java
index 55c2d77fbf..1a3bbe9e7c 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinInlineFilter.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinInlineFilter.java
@@ -19,7 +19,7 @@
/**
* Filters out instructions that were inlined by Kotlin compiler.
*/
-public final class KotlinInlineFilter implements IFilter {
+final class KotlinInlineFilter implements IFilter {
private int firstGeneratedLineNumber = -1;
@@ -29,7 +29,7 @@ public void filter(final MethodNode methodNode,
return;
}
- if (!KotlinGeneratedFilter.isKotlinClass(context)) {
+ if (!Filters.isKotlinClass(context)) {
return;
}
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
index 0dd736c57e..a098f48d17 100644
--- 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
@@ -22,7 +22,7 @@
* Filters branch in bytecode that Kotlin compiler generates for reading from
* lateinit properties.
*/
-public class KotlinLateinitFilter implements IFilter {
+final class KotlinLateinitFilter implements IFilter {
public void filter(final MethodNode methodNode,
final IFilterContext context, final IFilterOutput output) {
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinNotNullOperatorFilter.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinNotNullOperatorFilter.java
index e6a2964284..d8d377224a 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinNotNullOperatorFilter.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinNotNullOperatorFilter.java
@@ -20,7 +20,7 @@
* Filters branch in bytecode that Kotlin compiler generates for not-null
* assertion operator.
*/
-public final class KotlinNotNullOperatorFilter implements IFilter {
+final class KotlinNotNullOperatorFilter implements IFilter {
public void filter(final MethodNode methodNode,
final IFilterContext context, final IFilterOutput output) {
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinUnsafeCastOperatorFilter.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinUnsafeCastOperatorFilter.java
index fd03de534e..559bda4b21 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinUnsafeCastOperatorFilter.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinUnsafeCastOperatorFilter.java
@@ -22,11 +22,11 @@
* Filters branch in bytecode that Kotlin compiler generates for "unsafe" cast
* operator.
*/
-public final class KotlinUnsafeCastOperatorFilter implements IFilter {
+final class KotlinUnsafeCastOperatorFilter implements IFilter {
public void filter(final MethodNode methodNode,
final IFilterContext context, final IFilterOutput output) {
- if (!KotlinGeneratedFilter.isKotlinClass(context)) {
+ if (!Filters.isKotlinClass(context)) {
return;
}
final Matcher matcher = new Matcher();
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinWhenFilter.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinWhenFilter.java
index 202ed3bea6..dcd5e810f4 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinWhenFilter.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinWhenFilter.java
@@ -30,7 +30,7 @@
* expressions and statements with subject of type enum class or
* sealed class.
*/
-public final class KotlinWhenFilter implements IFilter {
+final class KotlinWhenFilter implements IFilter {
private static final String EXCEPTION = "kotlin/NoWhenBranchMatchedException";
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinWhenStringFilter.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinWhenStringFilter.java
index 5f34035dab..9946178dcd 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinWhenStringFilter.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinWhenStringFilter.java
@@ -28,7 +28,7 @@
* Filters bytecode that Kotlin compiler generates for when
* expressions with a String.
*/
-public final class KotlinWhenStringFilter implements IFilter {
+final class KotlinWhenStringFilter implements IFilter {
public void filter(final MethodNode methodNode,
final IFilterContext context, final IFilterOutput output) {
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/PrivateEmptyNoArgConstructorFilter.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/PrivateEmptyNoArgConstructorFilter.java
index c2a4e64322..084814bf53 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/PrivateEmptyNoArgConstructorFilter.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/PrivateEmptyNoArgConstructorFilter.java
@@ -18,7 +18,7 @@
/**
* Filters private empty constructors that do not have arguments.
*/
-public final class PrivateEmptyNoArgConstructorFilter implements IFilter {
+final class PrivateEmptyNoArgConstructorFilter implements IFilter {
private static final String CONSTRUCTOR_NAME = "";
private static final String CONSTRUCTOR_DESC = "()V";
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/RecordsFilter.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/RecordsFilter.java
index f7ab5c7f5d..8bedef2d09 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/RecordsFilter.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/RecordsFilter.java
@@ -22,7 +22,7 @@
* Filters methods toString, hashCode and
* equals that compiler generates for records.
*/
-public final class RecordsFilter implements IFilter {
+final class RecordsFilter implements IFilter {
public void filter(final MethodNode methodNode,
final IFilterContext context, final IFilterOutput output) {
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/StringSwitchFilter.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/StringSwitchFilter.java
index 16861854bb..10cf5917e2 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/StringSwitchFilter.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/StringSwitchFilter.java
@@ -29,7 +29,7 @@
* with a String and by Kotlin compiler 1.5 and above for a
* when expression with a String.
*/
-public final class StringSwitchFilter implements IFilter {
+final class StringSwitchFilter implements IFilter {
public void filter(final MethodNode methodNode,
final IFilterContext context, final IFilterOutput output) {
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/StringSwitchJavacFilter.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/StringSwitchJavacFilter.java
index cf48d00603..2dab00fad9 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/StringSwitchJavacFilter.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/StringSwitchJavacFilter.java
@@ -23,7 +23,7 @@
/**
* Filters code that is generated by javac for a switch statement with a String.
*/
-public final class StringSwitchJavacFilter implements IFilter {
+final class StringSwitchJavacFilter implements IFilter {
public void filter(final MethodNode methodNode,
final IFilterContext context, final IFilterOutput output) {
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/SynchronizedFilter.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/SynchronizedFilter.java
index 0e212a4890..35d7cff883 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/SynchronizedFilter.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/SynchronizedFilter.java
@@ -20,7 +20,7 @@
/**
* Filters code that is generated for synchronized statement.
*/
-public final class SynchronizedFilter implements IFilter {
+final class SynchronizedFilter implements IFilter {
public void filter(final MethodNode methodNode,
final IFilterContext context, final IFilterOutput output) {
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/SyntheticFilter.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/SyntheticFilter.java
index 55e35a621c..f260d01cf2 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/SyntheticFilter.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/SyntheticFilter.java
@@ -18,7 +18,7 @@
/**
* Filters synthetic methods unless they represent bodies of lambda expressions.
*/
-public final class SyntheticFilter implements IFilter {
+final class SyntheticFilter implements IFilter {
private static boolean isScalaClass(final IFilterContext context) {
return context.getClassAttributes().contains("ScalaSig")
@@ -41,7 +41,7 @@ public void filter(final MethodNode methodNode,
}
}
- if (KotlinGeneratedFilter.isKotlinClass(context)) {
+ if (Filters.isKotlinClass(context)) {
if (!methodNode.name.startsWith("access$")) {
return;
}
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/TryWithResourcesEcjFilter.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/TryWithResourcesEcjFilter.java
index ec560a457d..c59398c4be 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/TryWithResourcesEcjFilter.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/TryWithResourcesEcjFilter.java
@@ -26,7 +26,7 @@
/**
* Filters code that ECJ generates for try-with-resources statement.
*/
-public final class TryWithResourcesEcjFilter implements IFilter {
+final class TryWithResourcesEcjFilter implements IFilter {
public void filter(final MethodNode methodNode,
final IFilterContext context, final IFilterOutput output) {
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/TryWithResourcesJavac11Filter.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/TryWithResourcesJavac11Filter.java
index ddd24c2a3b..e381c85041 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/TryWithResourcesJavac11Filter.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/TryWithResourcesJavac11Filter.java
@@ -22,7 +22,7 @@
* Filters code which is generated for try-with-resources statement by javac
* starting from version 11.
*/
-public final class TryWithResourcesJavac11Filter implements IFilter {
+final class TryWithResourcesJavac11Filter implements IFilter {
public void filter(final MethodNode methodNode,
final IFilterContext context, final IFilterOutput output) {
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/TryWithResourcesJavacFilter.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/TryWithResourcesJavacFilter.java
index e56a556980..261f4b0c3b 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/TryWithResourcesJavacFilter.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/TryWithResourcesJavacFilter.java
@@ -22,7 +22,7 @@
* Filters code which is generated for try-with-resources statement by javac
* versions from 7 to 10.
*/
-public final class TryWithResourcesJavacFilter implements IFilter {
+final class TryWithResourcesJavacFilter implements IFilter {
public void filter(final MethodNode methodNode,
final IFilterContext context, final IFilterOutput output) {
From fac812ea0c5de6cc5721ce6f22f27d69525efcde Mon Sep 17 00:00:00 2001
From: Evgeny Mandrikov <138671+Godin@users.noreply.github.com>
Date: Mon, 9 Dec 2024 19:49:08 +0100
Subject: [PATCH 160/255] Add validation test for `synchronized` block in
Kotlin (#1703)
---
.../kotlin/KotlinSynchronizedTest.java | 27 +++++++++
.../targets/KotlinSynchronizedTarget.kt | 57 +++++++++++++++++++
2 files changed, 84 insertions(+)
create mode 100644 org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinSynchronizedTest.java
create mode 100644 org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinSynchronizedTarget.kt
diff --git a/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinSynchronizedTest.java b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinSynchronizedTest.java
new file mode 100644
index 0000000000..9bb2904b68
--- /dev/null
+++ b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinSynchronizedTest.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.KotlinSynchronizedTarget;
+
+/**
+ * Test of code coverage in {@link KotlinSynchronizedTarget}
+ */
+public class KotlinSynchronizedTest extends ValidationTestBase {
+
+ public KotlinSynchronizedTest() {
+ super(KotlinSynchronizedTarget.class);
+ }
+
+}
diff --git a/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinSynchronizedTarget.kt b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinSynchronizedTarget.kt
new file mode 100644
index 0000000000..7c52b03fd1
--- /dev/null
+++ b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinSynchronizedTarget.kt
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * 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.*
+
+/**
+ * Test target with [synchronized] block.
+ */
+object KotlinSynchronizedTarget {
+
+ private val lock = Any();
+
+ private fun normal() {
+ synchronized(lock) { // assertFullyCovered()
+ nop() // assertFullyCovered()
+ } // assertFullyCovered()
+ }
+
+ private fun explicitException() {
+ synchronized(lock) { // assertFullyCovered()
+ throw StubException() // assertFullyCovered()
+ } // assertEmpty()
+ }
+
+ private fun implicitException() {
+ synchronized(lock) { // assertPartlyCovered()
+ ex() // assertNotCovered()
+ } // assertNotCovered()
+ }
+
+ @JvmStatic
+ fun main(args: Array) {
+ normal()
+
+ try {
+ explicitException()
+ } catch (_: StubException) {
+ }
+
+ try {
+ implicitException()
+ } catch (_: StubException) {
+ }
+ }
+
+}
From e8f0907904649b98fb901b3834f9025813223570 Mon Sep 17 00:00:00 2001
From: Evgeny Mandrikov <138671+Godin@users.noreply.github.com>
Date: Mon, 9 Dec 2024 21:11:43 +0100
Subject: [PATCH 161/255] Add validation test for exception based control flow
in Kotlin (#1671)
---
.../kotlin/KotlinExceptionsTest.java | 27 ++++
.../kotlin/targets/KotlinExceptionsTarget.kt | 133 ++++++++++++++++++
2 files changed, 160 insertions(+)
create mode 100644 org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinExceptionsTest.java
create mode 100644 org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinExceptionsTarget.kt
diff --git a/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinExceptionsTest.java b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinExceptionsTest.java
new file mode 100644
index 0000000000..98cc5211c8
--- /dev/null
+++ b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinExceptionsTest.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.KotlinExceptionsTarget;
+
+/**
+ * Test of code coverage in {@link KotlinExceptionsTarget}.
+ */
+public class KotlinExceptionsTest extends ValidationTestBase {
+
+ public KotlinExceptionsTest() {
+ super(KotlinExceptionsTarget.class);
+ }
+
+}
diff --git a/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinExceptionsTarget.kt b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinExceptionsTarget.kt
new file mode 100644
index 0000000000..2b2438c884
--- /dev/null
+++ b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinExceptionsTarget.kt
@@ -0,0 +1,133 @@
+/*******************************************************************************
+ * 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.*
+
+/**
+ * Test target with exception based control flow examples.
+ */
+object KotlinExceptionsTarget {
+
+ private fun implicitArrayIndexOutOfBoundsException(a: Array) {
+ nop() // assertNotCovered()
+ a[0] // assertNotCovered()
+ nop() // assertNotCovered()
+ }
+
+ private fun implicitException() {
+ nop() // assertFullyCovered()
+ ex() // assertNotCovered()
+ nop() // assertNotCovered()
+ }
+
+ private fun explicitException() {
+ nop() // assertFullyCovered()
+ throw StubException() // assertFullyCovered()
+ }
+
+ private fun noExceptionTryCatch() {
+ nop() // assertFullyCovered()
+ try { // assertFullyCovered()
+ nop() // assertFullyCovered()
+ } catch (e: StubException) { // assertNotCovered()
+ nop() // assertNotCovered()
+ } // assertEmpty()
+ }
+
+ private fun implicitExceptionTryCatch() {
+ nop() // assertFullyCovered()
+ try { // assertFullyCovered()
+ nop() // assertFullyCovered()
+ ex() // assertNotCovered()
+ nop() // assertNotCovered()
+ } catch (e: StubException) { // assertFullyCovered()
+ nop() // assertFullyCovered()
+ } // assertEmpty()
+ }
+
+ private fun explicitExceptionTryCatch() {
+ nop() // assertFullyCovered()
+ try { // assertFullyCovered()
+ nop() // assertFullyCovered()
+ throw StubException() // assertFullyCovered()
+ } catch (e: StubException) { // assertFullyCovered()
+ nop() // assertFullyCovered()
+ } // assertEmpty()
+ }
+
+ private fun noExceptionFinally() {
+ nop() // assertFullyCovered()
+ try { // assertFullyCovered()
+ nop() // assertFullyCovered()
+ } finally { // assertEmpty()
+ nop() // assertFullyCovered()
+ } // assertEmpty()
+ }
+
+ private fun implicitExceptionFinally() {
+ nop() // assertFullyCovered()
+ try { // assertFullyCovered()
+ nop() // assertFullyCovered()
+ ex() // assertNotCovered()
+ nop() // assertNotCovered()
+ } finally { // assertEmpty()
+ nop() // assertFullyCovered()
+ } // assertEmpty()
+ }
+
+ private fun explicitExceptionFinally() {
+ nop() // assertFullyCovered()
+ try { // assertFullyCovered()
+ nop() // assertFullyCovered()
+ throw StubException() // assertFullyCovered()
+ } finally { // assertEmpty()
+ nop() // assertFullyCovered()
+ }
+ }
+
+ @JvmStatic
+ fun main(args: Array) {
+ try {
+ implicitArrayIndexOutOfBoundsException(arrayOf())
+ } catch (_: ArrayIndexOutOfBoundsException) {
+ }
+ try {
+ implicitException()
+ } catch (_: StubException) {
+ }
+ try {
+ explicitException()
+ } catch (_: StubException) {
+ }
+ noExceptionTryCatch()
+ try {
+ implicitExceptionTryCatch()
+ } catch (_: StubException) {
+ }
+ try {
+ explicitExceptionTryCatch()
+ } catch (_: StubException) {
+ }
+ noExceptionFinally()
+ try {
+ implicitExceptionFinally()
+ } catch (_: StubException) {
+ }
+ try {
+ explicitExceptionFinally()
+ } catch (_: StubException) {
+ }
+ }
+
+}
From e6f29c902242c24c20f7740e27006a83330b8e88 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 12 Dec 2024 15:13:49 +0100
Subject: [PATCH 162/255] Upgrade maven-javadoc-plugin to 3.11.2 (#1808)
Bump org.apache.maven.plugins:maven-javadoc-plugin in /org.jacoco.build
Bumps [org.apache.maven.plugins:maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) from 3.11.1 to 3.11.2.
- [Release notes](https://github.com/apache/maven-javadoc-plugin/releases)
- [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.11.1...maven-javadoc-plugin-3.11.2)
---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-javadoc-plugin
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
org.jacoco.build/pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/org.jacoco.build/pom.xml b/org.jacoco.build/pom.xml
index ba52aa121a..e3fbc9a7f7 100644
--- a/org.jacoco.build/pom.xml
+++ b/org.jacoco.build/pom.xml
@@ -353,7 +353,7 @@
org.apache.maven.pluginsmaven-javadoc-plugin
- 3.11.1
+ 3.11.2truefalse
From aa0ebe8eb44d6ffe010afe134b6f0deb34491e81 Mon Sep 17 00:00:00 2001
From: Evgeny Mandrikov <138671+Godin@users.noreply.github.com>
Date: Fri, 13 Dec 2024 10:09:16 +0100
Subject: [PATCH 163/255] Add validation test for chain of elvis operators in
Kotlin (#1812)
---
.../kotlin/KotlinElvisOperatorTest.java | 2 +-
.../targets/KotlinElvisOperatorTarget.kt | 49 +++++++++++++++++--
2 files changed, 45 insertions(+), 6 deletions(-)
diff --git a/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinElvisOperatorTest.java b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinElvisOperatorTest.java
index b732d4453e..72c18440a9 100644
--- a/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinElvisOperatorTest.java
+++ b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinElvisOperatorTest.java
@@ -16,7 +16,7 @@
import org.jacoco.core.test.validation.kotlin.targets.KotlinElvisOperatorTarget;
/**
- * Test of elvis operator.
+ * Test of code coverage in {@link KotlinElvisOperatorTarget}.
*/
public class KotlinElvisOperatorTest extends ValidationTestBase {
diff --git a/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinElvisOperatorTarget.kt b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinElvisOperatorTarget.kt
index 0310b6d072..c5d6b73315 100644
--- a/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinElvisOperatorTarget.kt
+++ b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinElvisOperatorTarget.kt
@@ -13,18 +13,57 @@
package org.jacoco.core.test.validation.kotlin.targets
/**
- * Test target for elvis operator.
+ * Test target for [elvis operator (`?:`)](https://kotlinlang.org/docs/null-safety.html#elvis-operator).
*/
object KotlinElvisOperatorTarget {
- private fun example(x: String?): String {
- return x ?: "" // assertFullyCovered(0, 2)
+ private fun elvis() {
+ fun nullOnly(a: String?): String =
+ a ?: "" // assertFullyCovered(1, 1)
+
+ fun nonNullOnly(a: String?): String =
+ a ?: "" // assertPartlyCovered(1, 1)
+
+ fun fullCoverage(a: String?): String =
+ a ?: "" // assertFullyCovered(0, 2)
+
+ nullOnly(null)
+ nonNullOnly("")
+ fullCoverage(null)
+ fullCoverage("")
+ }
+
+ private fun elvisChain() {
+ fun bothNull(a: String?, b: String?): String =
+ a ?: b ?: "" // assertFullyCovered(2, 2)
+
+ fun secondNonNull(a: String?, b: String?): String =
+ a ?: b ?: "" // assertPartlyCovered(2, 2)
+
+ fun firstNonNull(a: String?, b: String?): String =
+ a ?: b ?: "" // assertPartlyCovered(3, 1)
+
+ fun firstOrSecondNonNull(a: String?, b: String?): String =
+ a ?: b ?: "" // assertPartlyCovered(1, 3)
+
+ fun fullCoverage(a: String?, b: String?): String =
+ a ?: b ?: "" // assertFullyCovered(0, 4)
+
+ bothNull(null, null)
+ secondNonNull(null, "")
+ firstNonNull("", null)
+ firstNonNull("", "")
+ firstOrSecondNonNull("", null)
+ firstOrSecondNonNull(null, "")
+ fullCoverage(null, null)
+ fullCoverage("", null)
+ fullCoverage(null, "")
}
@JvmStatic
fun main(args: Array) {
- example("")
- example(null)
+ elvis()
+ elvisChain()
}
}
From 5d8e0855e3ce82cabf0042c668df41a1bce50095 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 13 Dec 2024 15:01:01 +0100
Subject: [PATCH 164/255] Upgrade sonar-maven-plugin to 5.0.0.4389 (#1771)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Evgeny Mandrikov
---
.azure-pipelines/azure-pipelines.yml | 2 +-
org.jacoco.build/pom.xml | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/.azure-pipelines/azure-pipelines.yml b/.azure-pipelines/azure-pipelines.yml
index 155504492b..7ed12a5f88 100644
--- a/.azure-pipelines/azure-pipelines.yml
+++ b/.azure-pipelines/azure-pipelines.yml
@@ -93,7 +93,7 @@ jobs:
elif [[ "$BUILD_SOURCEBRANCH" == "refs/heads/master" && "$JDK_VERSION" == "11" ]]; then
./mvnw -V -B -e --no-transfer-progress -f org.jacoco.build \
verify -Djdk.version=$JDK_VERSION -Dbytecode.version=$JDK_VERSION \
- sonar:sonar \
+ sonar:sonar -Dsonar.scanner.skipJreProvisioning \
--toolchains=toolchains.xml --settings=.azure-pipelines/maven-settings.xml
else
./mvnw -V -B -e --no-transfer-progress \
diff --git a/org.jacoco.build/pom.xml b/org.jacoco.build/pom.xml
index e3fbc9a7f7..4fa5493de5 100644
--- a/org.jacoco.build/pom.xml
+++ b/org.jacoco.build/pom.xml
@@ -455,7 +455,7 @@
org.sonarsource.scanner.mavensonar-maven-plugin
- 4.0.0.4121
+ 5.0.0.4389
From 6a1a43010d24eb79617b35795fd474d39a4f45a6 Mon Sep 17 00:00:00 2001
From: Evgeny Mandrikov <138671+Godin@users.noreply.github.com>
Date: Sun, 15 Dec 2024 23:43:45 +0100
Subject: [PATCH 165/255] Add filter for Kotlin safe call operators chains
(#1810)
---
.../kotlin/KotlinSafeCallOperatorTest.java | 2 +-
.../targets/KotlinSafeCallOperatorTarget.kt | 41 +++++++--
.../analysis/filter/FilterTestBase.java | 8 +-
.../KotlinSafeCallOperatorFilterTest.java | 83 +++++++++++++++++
.../internal/analysis/filter/Filters.java | 1 +
.../filter/KotlinSafeCallOperatorFilter.java | 90 +++++++++++++++++++
org.jacoco.doc/docroot/doc/changes.html | 3 +
7 files changed, 220 insertions(+), 8 deletions(-)
create mode 100644 org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinSafeCallOperatorFilterTest.java
create mode 100644 org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinSafeCallOperatorFilter.java
diff --git a/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinSafeCallOperatorTest.java b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinSafeCallOperatorTest.java
index 203f62f3c9..ad5a25b31e 100644
--- a/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinSafeCallOperatorTest.java
+++ b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinSafeCallOperatorTest.java
@@ -16,7 +16,7 @@
import org.jacoco.core.test.validation.kotlin.targets.KotlinSafeCallOperatorTarget;
/**
- * Test of safe call operator.
+ * Test of code coverage in {@link KotlinSafeCallOperatorTarget}.
*/
public class KotlinSafeCallOperatorTest extends ValidationTestBase {
diff --git a/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinSafeCallOperatorTarget.kt b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinSafeCallOperatorTarget.kt
index ec8dd708c9..96c94559fc 100644
--- a/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinSafeCallOperatorTarget.kt
+++ b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinSafeCallOperatorTarget.kt
@@ -13,18 +13,49 @@
package org.jacoco.core.test.validation.kotlin.targets
/**
- * Test target for safe call operator.
+ * Test target for [safe call operator (`?.`)](https://kotlinlang.org/docs/null-safety.html#safe-call-operator).
*/
object KotlinSafeCallOperatorTarget {
- private fun example(x: String?): Int? {
- return x?.length // assertFullyCovered(0, 2)
+ data class A(val b: B)
+ data class B(val c: String)
+
+ private fun safeCall() {
+ fun nullOnly(b: B?): String? =
+ b?.c // assertPartlyCovered(1, 1)
+
+ fun nonNullOnly(b: B?): String? =
+ b?.c // assertPartlyCovered(1, 1)
+
+ fun fullCoverage(b: B?): String? =
+ b?.c // assertFullyCovered(0, 2)
+
+ nullOnly(null)
+ nonNullOnly(B(""))
+ fullCoverage(null)
+ fullCoverage(B(""))
+ }
+
+ private fun safeCallChain() {
+ fun nullOnly(a: A?): String? =
+ a?.b?.c // assertPartlyCovered(2, 2)
+
+ fun nonNullOnly(a: A?): String? =
+ a?.b?.c // assertPartlyCovered(2, 2)
+
+ fun fullCoverage(a: A?): String? =
+ a?.b?.c // assertFullyCovered(0, 4)
+
+ nullOnly(null)
+ nonNullOnly(A(B("")))
+ fullCoverage(null)
+ fullCoverage(A(B("")))
}
@JvmStatic
fun main(args: Array) {
- example("")
- example(null)
+ safeCall()
+ safeCallChain()
}
}
diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/FilterTestBase.java b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/FilterTestBase.java
index 66162e5ad0..0e24547a11 100644
--- a/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/FilterTestBase.java
+++ b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/FilterTestBase.java
@@ -73,8 +73,12 @@ final void assertNoReplacedBranches() {
final void assertReplacedBranches(final AbstractInsnNode source,
final Set newTargets) {
- assertEquals(Collections.singletonMap(source, newTargets),
- replacedBranches);
+ assertReplacedBranches(Collections.singletonMap(source, newTargets));
+ }
+
+ final void assertReplacedBranches(
+ final Map> expected) {
+ assertEquals(expected, replacedBranches);
}
static class Range {
diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinSafeCallOperatorFilterTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinSafeCallOperatorFilterTest.java
new file mode 100644
index 0000000000..9e0dc31290
--- /dev/null
+++ b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinSafeCallOperatorFilterTest.java
@@ -0,0 +1,83 @@
+/*******************************************************************************
+ * 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 java.util.HashMap;
+import java.util.HashSet;
+import java.util.Set;
+
+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;
+
+/**
+ * Unit tests for {@link KotlinSafeCallOperatorFilter}.
+ */
+public class KotlinSafeCallOperatorFilterTest extends FilterTestBase {
+
+ private final IFilter filter = new KotlinSafeCallOperatorFilter();
+
+ /**
+ *
+ * data class A(val b: B)
+ * data class B(val c: String)
+ * fun example(a: A?): String? =
+ * a?.b?.c
+ *
+ *
+ * https://github.com/JetBrains/kotlin/commit/0a67ab54fec635f82e0507cbdd4299ae0dbe71b0
+ */
+ @Test
+ public void should_filter_safe_call_chain() {
+ context.classAnnotations
+ .add(KotlinGeneratedFilter.KOTLIN_METADATA_DESC);
+ final MethodNode m = new MethodNode(InstrSupport.ASM_API_VERSION, 0,
+ "example", "(LA;)Ljava/lang/String;", null, null);
+ m.visitVarInsn(Opcodes.ALOAD, 1);
+ final Label label1 = new Label();
+ final Label label2 = new Label();
+
+ m.visitInsn(Opcodes.DUP);
+ m.visitJumpInsn(Opcodes.IFNULL, label1);
+ final AbstractInsnNode i1 = m.instructions.getLast();
+ m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "A", "getB", "()LB;", false);
+
+ m.visitInsn(Opcodes.DUP);
+ m.visitJumpInsn(Opcodes.IFNULL, label1);
+ final AbstractInsnNode i2 = m.instructions.getLast();
+ m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "B", "getC",
+ "()Ljava/lang/String;", false);
+ final HashSet r = new HashSet();
+ r.add(m.instructions.getLast());
+
+ m.visitJumpInsn(Opcodes.GOTO, label2);
+
+ m.visitLabel(label1);
+ m.visitInsn(Opcodes.POP);
+ r.add(m.instructions.getLast());
+ m.visitInsn(Opcodes.ACONST_NULL);
+ m.visitLabel(label2);
+
+ filter.filter(m, context, output);
+
+ assertIgnored();
+ final HashMap> expected = new HashMap>();
+ expected.put(i1, r);
+ expected.put(i2, r);
+ assertReplacedBranches(expected);
+ }
+
+}
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 11b748caf5..1f7e600edd 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
@@ -44,6 +44,7 @@ public static IFilter all() {
new RecordPatternFilter(), //
new AnnotationGeneratedFilter(), new KotlinGeneratedFilter(),
new KotlinEnumFilter(), //
+ new KotlinSafeCallOperatorFilter(), //
new KotlinLateinitFilter(), new KotlinWhenFilter(),
new KotlinWhenStringFilter(),
new KotlinUnsafeCastOperatorFilter(),
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinSafeCallOperatorFilter.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinSafeCallOperatorFilter.java
new file mode 100644
index 0000000000..887ff7ad87
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinSafeCallOperatorFilter.java
@@ -0,0 +1,90 @@
+/*******************************************************************************
+ * 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 java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.tree.AbstractInsnNode;
+import org.objectweb.asm.tree.JumpInsnNode;
+import org.objectweb.asm.tree.LabelNode;
+import org.objectweb.asm.tree.MethodNode;
+
+/**
+ * Filters bytecode that Kotlin compiler generates for chains of safe call
+ * operators ({@code ?.}).
+ */
+final class KotlinSafeCallOperatorFilter implements IFilter {
+
+ public void filter(final MethodNode methodNode,
+ final IFilterContext context, final IFilterOutput output) {
+ if (!Filters.isKotlinClass(context)) {
+ return;
+ }
+ for (final ArrayList chain : findChains(methodNode)) {
+ if (chain.size() == 1) {
+ continue;
+ }
+ JumpInsnNode lastJump = chain.get(chain.size() - 1);
+ final HashSet newTargets = new HashSet();
+ newTargets.add(AbstractMatcher.skipNonOpcodes(lastJump.getNext()));
+ newTargets.add(AbstractMatcher.skipNonOpcodes(lastJump.label));
+ for (final AbstractInsnNode i : chain) {
+ output.replaceBranches(i, newTargets);
+ }
+ }
+ }
+
+ /**
+ *
+ */
+ @Test
+ public void should_filter_Kotlin_nullable_case() {
+ final Range range = new Range();
+ final Set expectedNewTargets = new HashSet();
+
+ final MethodNode m = new MethodNode(InstrSupport.ASM_API_VERSION, 0,
+ "example", "(Ljava/lang/String;)Ljava/lang/String;", null,
+ null);
+ final Label hashA = new Label();
+ final Label hashB = new Label();
+ final Label hashC = new Label();
+ final Label caseA = new Label();
+ final Label caseB = new Label();
+ final Label caseC = new Label();
+ final Label caseNull = new Label();
+ final Label caseElse = new Label();
+ final Label end = new Label();
+
+ m.visitVarInsn(Opcodes.ALOAD, 1);
+ m.visitVarInsn(Opcodes.ASTORE, 2);
+ m.visitVarInsn(Opcodes.ALOAD, 2);
+ range.fromInclusive = m.instructions.getLast();
+ m.visitInsn(Opcodes.DUP);
+ m.visitJumpInsn(Opcodes.IFNULL, caseNull);
+ m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/String", "hashCode",
+ "()I", false);
+ m.visitTableSwitchInsn(97, 99, caseElse,
+ new Label[] { hashA, hashB, hashC });
+
+ m.visitLabel(hashA);
+ m.visitVarInsn(Opcodes.ALOAD, 2);
+ m.visitLdcInsn("a");
+ m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/String", "equals",
+ "(Ljava/lang/Object;)Z", false);
+ m.visitJumpInsn(Opcodes.IFNE, caseA);
+ m.visitJumpInsn(Opcodes.GOTO, caseElse);
+
+ m.visitLabel(hashB);
+ m.visitVarInsn(Opcodes.ALOAD, 2);
+ m.visitLdcInsn("b");
+ m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/String", "equals",
+ "(Ljava/lang/Object;)Z", false);
+ m.visitJumpInsn(Opcodes.IFNE, caseB);
+ m.visitJumpInsn(Opcodes.GOTO, caseElse);
+
+ m.visitLabel(hashC);
+ m.visitVarInsn(Opcodes.ALOAD, 2);
+ m.visitLdcInsn("c");
+ m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/String", "equals",
+ "(Ljava/lang/Object;)Z", false);
+ m.visitJumpInsn(Opcodes.IFNE, caseC);
+ m.visitJumpInsn(Opcodes.GOTO, caseElse);
+ range.toInclusive = m.instructions.getLast();
+
+ m.visitLabel(caseA);
+ m.visitLdcInsn("case a");
+ expectedNewTargets.add(m.instructions.getLast());
+ m.visitJumpInsn(Opcodes.GOTO, end);
+
+ m.visitLabel(caseB);
+ m.visitLdcInsn("case b");
+ expectedNewTargets.add(m.instructions.getLast());
+ m.visitJumpInsn(Opcodes.GOTO, end);
+
+ m.visitLabel(caseC);
+ m.visitLdcInsn("case c");
+ expectedNewTargets.add(m.instructions.getLast());
+ m.visitJumpInsn(Opcodes.GOTO, end);
+
+ m.visitLabel(caseNull);
+ m.visitInsn(Opcodes.POP);
+ expectedNewTargets.add(m.instructions.getLast());
+ m.visitLdcInsn("null");
+ m.visitJumpInsn(Opcodes.GOTO, end);
+
+ m.visitLabel(caseElse);
+ m.visitLdcInsn("else");
+ expectedNewTargets.add(m.instructions.getLast());
+
+ m.visitLabel(end);
+ m.visitInsn(Opcodes.ARETURN);
+
+ filter.filter(m, context, output);
+
+ assertIgnored(range);
+ assertReplacedBranches(range.fromInclusive.getPrevious(),
expectedNewTargets);
}
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/StringSwitchFilter.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/StringSwitchFilter.java
index 10cf5917e2..21ab863e92 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/StringSwitchFilter.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/StringSwitchFilter.java
@@ -42,19 +42,35 @@ public void filter(final MethodNode methodNode,
private static class Matcher extends AbstractMatcher {
public void match(final AbstractInsnNode start,
final IFilterOutput output) {
-
- if (start.getOpcode() != /* ECJ */ Opcodes.ASTORE
- && start.getOpcode() != /* Kotlin */ Opcodes.ALOAD) {
+ if (start.getOpcode() != Opcodes.ASTORE) {
return;
}
+ vars.put("s", (VarInsnNode) start);
cursor = start;
+ JumpInsnNode ifNullInstruction = null;
+ if (start.getNext().getOpcode() == Opcodes.ALOAD) {
+ // Kotlin
+ nextIsVar(Opcodes.ALOAD, "s");
+ if (cursor == null) {
+ return;
+ } else if (cursor.getNext().getOpcode() == Opcodes.DUP) {
+ // nullable case
+ nextIs(Opcodes.DUP);
+ nextIs(Opcodes.IFNULL);
+ ifNullInstruction = (JumpInsnNode) cursor;
+ } else if (cursor.getNext().getOpcode() == Opcodes.IFNULL) {
+ // nullable else
+ nextIs(Opcodes.IFNULL);
+ ifNullInstruction = (JumpInsnNode) cursor;
+ nextIsVar(Opcodes.ALOAD, "s");
+ }
+ }
nextIsInvoke(Opcodes.INVOKEVIRTUAL, "java/lang/String", "hashCode",
"()I");
nextIsSwitch();
if (cursor == null) {
return;
}
- vars.put("s", (VarInsnNode) start);
final AbstractInsnNode s = cursor;
final int hashCodes;
@@ -102,8 +118,12 @@ public void match(final AbstractInsnNode start,
}
}
- output.ignore(s.getNext(), cursor);
- output.replaceBranches(s, replacements);
+ if (ifNullInstruction != null) {
+ replacements.add(skipNonOpcodes(ifNullInstruction.label));
+ }
+
+ output.ignore(start.getNext(), cursor);
+ output.replaceBranches(start, replacements);
}
}
diff --git a/org.jacoco.doc/docroot/doc/changes.html b/org.jacoco.doc/docroot/doc/changes.html
index e098ee6d0d..f3b316c087 100644
--- a/org.jacoco.doc/docroot/doc/changes.html
+++ b/org.jacoco.doc/docroot/doc/changes.html
@@ -49,6 +49,10 @@
New Features
expressions and statements with nullable enum subject is filtered out during
generation of report
(GitHub #1774).
+
Part of bytecode generated by the Kotlin compiler for when
+ expressions and statements with nullable String subject is filtered out during
+ generation of report
+ (GitHub #1769).
Part of bytecode generated by the Kotlin compiler for chains of safe call
operators is filtered out during generation of report
(GitHub #1810).
The minimum supported JRE version for JaCoCo is Java 5. To guarantee
compatibility JaCoCo release builds should always be executed using JDK 5.
In addition we run builds with 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17
- 18, 19, 20, 21, 22 and 23 JDKs.
+ 18, 19, 20, 21, 22, 23, 24 and 25 JDKs.
+ */
+ @Test
+ public void kotlin_safe_call_elvis_with_filter_should_show_full_branch_coverage_when_both_cases_covered() {
+ createKotlinSafeCallElvis();
+ // non-null case
+ probes[1] = true;
+ probes[4] = true;
+ // null case
+ probes[0] = true;
+ probes[3] = true;
+ runMethodAnalzer(KOTLIN_SAFE_CALL_ELVIS_FILTER);
+
+ assertLine(1001, 0, 9, 0, 4);
+ }
+
+ @Test
+ public void kotlin_safe_call_elvis_without_filter() {
+ createKotlinSafeCallElvis();
+ // non-null case
+ probes[1] = true;
+ probes[4] = true;
+ // null case
+ probes[0] = true;
+ probes[3] = true;
+ runMethodAnalzer();
+
+ assertLine(1001, 0, 9, 1, 3);
+ }
+
+ // === Scenario: table switch ===
private void createTableSwitch() {
final Label l0 = new Label();
@@ -559,41 +689,6 @@ public void table_switch_should_create_4_probes() {
assertEquals(4, nextProbeId);
}
- private static final IFilter SWITCH_FILTER = new IFilter() {
- public void filter(final MethodNode methodNode,
- final IFilterContext context, final IFilterOutput output) {
- final AbstractInsnNode i = methodNode.instructions.get(3);
- assertEquals(Opcodes.TABLESWITCH, i.getOpcode());
- final AbstractInsnNode t1 = methodNode.instructions.get(6);
- assertEquals(Opcodes.BIPUSH, t1.getOpcode());
- final AbstractInsnNode t2 = methodNode.instructions.get(13);
- assertEquals(Opcodes.BIPUSH, t2.getOpcode());
-
- final Set newTargets = new HashSet();
- newTargets.add(t1);
- newTargets.add(t2);
- output.replaceBranches(i, newTargets);
- }
- };
-
- @Test
- public void table_switch_with_filter_should_show_2_branches_when_original_replaced() {
- createTableSwitch();
- runMethodAnalzer(SWITCH_FILTER);
-
- assertLine(1001, 2, 0, 2, 0);
- }
-
- @Test
- public void table_switch_with_filter_should_show_full_branch_coverage_when_new_targets_covered() {
- createTableSwitch();
- probes[0] = true;
- probes[1] = true;
- runMethodAnalzer(SWITCH_FILTER);
-
- assertLine(1001, 0, 2, 0, 2);
- }
-
@Test
public void table_switch_should_show_missed_when_no_probes_are_executed() {
createTableSwitch();
diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/MethodCoverageCalculatorTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/MethodCoverageCalculatorTest.java
index b0f41b8388..8613091adc 100644
--- a/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/MethodCoverageCalculatorTest.java
+++ b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/MethodCoverageCalculatorTest.java
@@ -14,12 +14,11 @@
import static org.junit.Assert.assertEquals;
-import java.util.Arrays;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.Map;
import org.jacoco.core.analysis.ISourceNode;
+import org.jacoco.core.internal.analysis.filter.Replacements;
import org.junit.Before;
import org.junit.Test;
import org.objectweb.asm.Opcodes;
@@ -173,17 +172,20 @@ public void should_merge_instructions_redundant() {
@Test
public void should_replace_branches() {
InsnNode i1 = addInsn(1);
- InsnNode i2 = addInsn(2, true);
+ InsnNode i2 = addInsn(2, false, true);
InsnNode i3 = addInsn(2, true);
InsnNode i4 = addInsn(2, false);
MethodCoverageCalculator c = new MethodCoverageCalculator(instructions);
- c.replaceBranches(i1,
- new HashSet(Arrays.asList(i2, i3, i4)));
+ Replacements replacements = new Replacements();
+ replacements.add(i2, i2, 1);
+ replacements.add(i3, i3, 0);
+ replacements.add(i4, i4, 0);
+ c.replaceBranches(i1, replacements);
c.calculate(coverage);
assertLine(1, 0, 1, 1, 2); // branches coverage status replaced
- assertLine(2, 1, 2, 0, 0); // still in place
+ assertLine(2, 1, 2, 1, 1); // still in place
}
@Test
@@ -196,8 +198,11 @@ public void should_replace_branches_with_merged_instructions() {
MethodCoverageCalculator c = new MethodCoverageCalculator(instructions);
c.merge(i4, i3);
c.merge(i3, i2);
- c.replaceBranches(i1,
- new HashSet(Arrays.asList(i2, i3, i4)));
+ Replacements replacements = new Replacements();
+ replacements.add(i2, i2, 0);
+ replacements.add(i3, i3, 0);
+ replacements.add(i4, i4, 0);
+ c.replaceBranches(i1, replacements);
c.calculate(coverage);
assertLine(1, 0, 1, 0, 3);
diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/ExhaustiveSwitchFilterTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/ExhaustiveSwitchFilterTest.java
index f9429192e5..d3d4e4c3d9 100644
--- a/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/ExhaustiveSwitchFilterTest.java
+++ b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/ExhaustiveSwitchFilterTest.java
@@ -12,8 +12,7 @@
*******************************************************************************/
package org.jacoco.core.internal.analysis.filter;
-import java.util.HashSet;
-import java.util.Set;
+import java.util.ArrayList;
import org.jacoco.core.internal.instr.InstrSupport;
import org.junit.Test;
@@ -29,15 +28,17 @@ public class ExhaustiveSwitchFilterTest extends FilterTestBase {
private final IFilter filter = new ExhaustiveSwitchFilter();
+ private final ArrayList replacements = new ArrayList();
+
/**
*
* enum E {
- * A, B, C
+ * A, B, C, D
* }
*
* int example(E e) {
* return switch (e) {
- * case A -> 1;
+ * case A, D -> 1;
* case B -> 2;
* case C -> 3;
* };
@@ -64,10 +65,12 @@ public void should_filter_when_default_branch_has_LineNumber_of_switch() {
final Label case1 = new Label();
final Label case2 = new Label();
final Label case3 = new Label();
- m.visitLookupSwitchInsn(dflt, new int[] { 1, 2, 3 },
- new Label[] { case1, case2, case3 });
+ m.visitLookupSwitchInsn(dflt, new int[] { 1, 2, 3, 4 },
+ new Label[] { case1, case2, case3, case1 });
final AbstractInsnNode switchNode = m.instructions.getLast();
- final Set newTargets = new HashSet();
+ replacements.add(new Replacement(0, switchNode, 1));
+ replacements.add(new Replacement(1, switchNode, 2));
+ replacements.add(new Replacement(2, switchNode, 3));
m.visitLabel(dflt);
final Range range = new Range();
@@ -83,16 +86,13 @@ public void should_filter_when_default_branch_has_LineNumber_of_switch() {
m.visitLabel(case1);
m.visitInsn(Opcodes.ICONST_1);
- newTargets.add(m.instructions.getLast());
m.visitJumpInsn(Opcodes.GOTO, end);
m.visitLabel(case2);
m.visitInsn(Opcodes.ICONST_2);
- newTargets.add(m.instructions.getLast());
m.visitLabel(case3);
m.visitInsn(Opcodes.ICONST_3);
- newTargets.add(m.instructions.getLast());
m.visitLabel(end);
m.visitInsn(Opcodes.IRETURN);
@@ -100,18 +100,18 @@ public void should_filter_when_default_branch_has_LineNumber_of_switch() {
filter.filter(m, context, output);
assertIgnored(range);
- assertReplacedBranches(switchNode, newTargets);
+ assertReplacedBranches(m, switchNode, replacements);
}
/**
*
* enum E {
- * A, B, C
+ * A, B, C, D
* }
*
* int example(E e) {
* return switch (e) {
- * case A -> 1;
+ * case A, D -> 1;
* case B -> 2;
* case C -> 3;
* };
@@ -138,10 +138,12 @@ public void should_filter_when_default_branch_has_no_LineNumber() {
final Label case1 = new Label();
final Label case2 = new Label();
final Label case3 = new Label();
- m.visitLookupSwitchInsn(dflt, new int[] { 1, 2, 3 },
- new Label[] { case1, case2, case3 });
+ m.visitLookupSwitchInsn(dflt, new int[] { 1, 2, 3, 4 },
+ new Label[] { case1, case2, case3, case1 });
final AbstractInsnNode switchNode = m.instructions.getLast();
- final Set newTargets = new HashSet();
+ replacements.add(new Replacement(0, switchNode, 1));
+ replacements.add(new Replacement(1, switchNode, 2));
+ replacements.add(new Replacement(2, switchNode, 3));
m.visitLabel(dflt);
final Range range = new Range();
@@ -156,16 +158,13 @@ public void should_filter_when_default_branch_has_no_LineNumber() {
m.visitLabel(case1);
m.visitInsn(Opcodes.ICONST_1);
- newTargets.add(m.instructions.getLast());
m.visitJumpInsn(Opcodes.GOTO, end);
m.visitLabel(case2);
m.visitInsn(Opcodes.ICONST_2);
- newTargets.add(m.instructions.getLast());
m.visitLabel(case3);
m.visitInsn(Opcodes.ICONST_3);
- newTargets.add(m.instructions.getLast());
m.visitLabel(end);
m.visitInsn(Opcodes.IRETURN);
@@ -173,18 +172,18 @@ public void should_filter_when_default_branch_has_no_LineNumber() {
filter.filter(m, context, output);
assertIgnored(range);
- assertReplacedBranches(switchNode, newTargets);
+ assertReplacedBranches(m, switchNode, replacements);
}
/**
*
* enum E {
- * A, B, C
+ * A, B, C, D
* }
*
* int example(E e) {
* return switch (e) {
- * case A -> 1;
+ * case A, D -> 1;
* case B -> 2;
* case C -> 3;
* };
@@ -211,10 +210,12 @@ public void should_filter_when_default_branch_throws_Java_21_MatchException() {
final Label case1 = new Label();
final Label case2 = new Label();
final Label case3 = new Label();
- m.visitLookupSwitchInsn(dflt, new int[] { 1, 2, 3 },
- new Label[] { case1, case2, case3 });
+ m.visitLookupSwitchInsn(dflt, new int[] { 1, 2, 3, 4 },
+ new Label[] { case1, case2, case3, case1 });
final AbstractInsnNode switchNode = m.instructions.getLast();
- final Set newTargets = new HashSet();
+ replacements.add(new Replacement(0, switchNode, 1));
+ replacements.add(new Replacement(1, switchNode, 2));
+ replacements.add(new Replacement(2, switchNode, 3));
m.visitLabel(dflt);
final Range range = new Range();
@@ -230,16 +231,13 @@ public void should_filter_when_default_branch_throws_Java_21_MatchException() {
m.visitLabel(case1);
m.visitInsn(Opcodes.ICONST_1);
- newTargets.add(m.instructions.getLast());
m.visitJumpInsn(Opcodes.GOTO, end);
m.visitLabel(case2);
m.visitInsn(Opcodes.ICONST_2);
- newTargets.add(m.instructions.getLast());
m.visitLabel(case3);
m.visitInsn(Opcodes.ICONST_3);
- newTargets.add(m.instructions.getLast());
m.visitLabel(end);
m.visitInsn(Opcodes.IRETURN);
@@ -247,7 +245,7 @@ public void should_filter_when_default_branch_throws_Java_21_MatchException() {
filter.filter(m, context, output);
assertIgnored(range);
- assertReplacedBranches(switchNode, newTargets);
+ assertReplacedBranches(m, switchNode, replacements);
}
/**
diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/FilterTestBase.java b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/FilterTestBase.java
index b9210dd004..7e27582131 100644
--- a/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/FilterTestBase.java
+++ b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/FilterTestBase.java
@@ -18,11 +18,12 @@
import static org.junit.Assert.fail;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.Set;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.MethodNode;
@@ -36,7 +37,7 @@ public abstract class FilterTestBase {
private final List ignoredRanges = new ArrayList();
- private final Map> replacedBranches = new HashMap>();
+ private final HashMap>> actualReplacements = new HashMap>>();
protected final IFilterOutput output = new IFilterOutput() {
public void ignore(final AbstractInsnNode fromInclusive,
@@ -52,9 +53,9 @@ public void merge(final AbstractInsnNode i1,
fail();
}
- public void replaceBranches(final AbstractInsnNode source,
- final Set newTargets) {
- replacedBranches.put(source, newTargets);
+ public void replaceBranches(AbstractInsnNode source,
+ Replacements replacements) {
+ actualReplacements.put(source, replacements.values());
}
};
@@ -68,17 +69,78 @@ final void assertMethodIgnored(final MethodNode m) {
}
final void assertNoReplacedBranches() {
- assertTrue(replacedBranches.isEmpty());
+ assertTrue(actualReplacements.isEmpty());
}
- final void assertReplacedBranches(final AbstractInsnNode source,
- final Set newTargets) {
- assertReplacedBranches(Collections.singletonMap(source, newTargets));
+ final void assertReplacedBranches(final MethodNode methodNode,
+ final AbstractInsnNode source,
+ final List expectedReplacements) {
+ assertReplacedBranches(methodNode,
+ Collections.singletonMap(source, expectedReplacements));
}
- final void assertReplacedBranches(
- final Map> expected) {
- assertEquals(expected, replacedBranches);
+ final void assertReplacedBranches(final MethodNode methodNode,
+ final Map> expectedReplacements) {
+ assertEquals(expectedReplacements.size(), actualReplacements.size());
+ for (final Map.Entry> entry : expectedReplacements
+ .entrySet()) {
+ final AbstractInsnNode node = entry.getKey();
+ final List replacements = entry.getValue();
+ assertReplacements(methodNode, node, replacements);
+ }
+ }
+
+ private void assertReplacements(final MethodNode methodNode,
+ final AbstractInsnNode source,
+ final List expectedReplacements) {
+
+ Collections.sort(expectedReplacements, new Comparator() {
+ public int compare(final Replacement r1, final Replacement r2) {
+ if (r1.newBranch == r2.newBranch) {
+ return r1.branch - r2.branch;
+ }
+ return r1.newBranch - r2.newBranch;
+ }
+ });
+
+ final StringBuilder expectedStringBuilder = new StringBuilder();
+ for (final Replacement replacement : expectedReplacements) {
+ expectedStringBuilder.append(replacement.newBranch)
+ .append(" if branch ").append(replacement.branch)
+ .append(" of instruction ").append(methodNode.instructions
+ .indexOf(replacement.instruction))
+ .append("\n");
+ }
+
+ final StringBuilder actualStringBuilder = new StringBuilder();
+ int newBranch = 0;
+ for (final Collection pairs : actualReplacements
+ .get(source)) {
+ for (Replacements.InstructionBranch pair : pairs) {
+ actualStringBuilder.append(newBranch).append(" if branch ")
+ .append(pair.branch).append(" of instruction ")
+ .append(methodNode.instructions
+ .indexOf(pair.instruction))
+ .append("\n");
+ }
+ newBranch++;
+ }
+
+ assertEquals(expectedStringBuilder.toString(),
+ actualStringBuilder.toString());
+ }
+
+ static class Replacement {
+ final int newBranch;
+ final AbstractInsnNode instruction;
+ final int branch;
+
+ Replacement(final int newBranch, final AbstractInsnNode instruction,
+ final int branch) {
+ this.newBranch = newBranch;
+ this.instruction = instruction;
+ this.branch = branch;
+ }
}
static class Range {
diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/FinallyFilterTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/FinallyFilterTest.java
index 876ff161fa..76ccafcd21 100644
--- a/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/FinallyFilterTest.java
+++ b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/FinallyFilterTest.java
@@ -422,8 +422,8 @@ public void merge(final AbstractInsnNode i1, final AbstractInsnNode i2) {
}
}
- public void replaceBranches(final AbstractInsnNode source,
- final Set newTargets) {
+ public void replaceBranches(AbstractInsnNode source,
+ Replacements replacements) {
fail();
}
diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinSafeCallOperatorFilterTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinSafeCallOperatorFilterTest.java
index 3ec105136b..2c6455fc8d 100644
--- a/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinSafeCallOperatorFilterTest.java
+++ b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinSafeCallOperatorFilterTest.java
@@ -12,9 +12,9 @@
*******************************************************************************/
package org.jacoco.core.internal.analysis.filter;
+import java.util.Arrays;
import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Set;
+import java.util.List;
import org.jacoco.core.internal.instr.InstrSupport;
import org.junit.Test;
@@ -52,32 +52,34 @@ public void should_filter_optimized_safe_call_chain() {
m.visitInsn(Opcodes.DUP);
m.visitJumpInsn(Opcodes.IFNULL, label1);
- final AbstractInsnNode i1 = m.instructions.getLast();
+ final AbstractInsnNode ifNullInstruction1 = m.instructions.getLast();
m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "A", "getB", "()LB;", false);
m.visitInsn(Opcodes.DUP);
m.visitJumpInsn(Opcodes.IFNULL, label1);
- final AbstractInsnNode i2 = m.instructions.getLast();
+ final AbstractInsnNode ifNullInstruction2 = m.instructions.getLast();
m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "B", "getC",
"()Ljava/lang/String;", false);
- final HashSet r = new HashSet();
- r.add(m.instructions.getLast());
m.visitJumpInsn(Opcodes.GOTO, label2);
m.visitLabel(label1);
m.visitInsn(Opcodes.POP);
- r.add(m.instructions.getLast());
+ final AbstractInsnNode popInstruction = m.instructions.getLast();
m.visitInsn(Opcodes.ACONST_NULL);
m.visitLabel(label2);
filter.filter(m, context, output);
assertIgnored();
- final HashMap> expected = new HashMap>();
- expected.put(i1, r);
- expected.put(i2, r);
- assertReplacedBranches(expected);
+ final HashMap> replacements = new HashMap>();
+ replacements.put(ifNullInstruction1, Arrays.asList( //
+ new Replacement(0, ifNullInstruction2, 0),
+ new Replacement(1, popInstruction, 0)));
+ replacements.put(ifNullInstruction2, Arrays.asList( //
+ new Replacement(0, ifNullInstruction2, 0),
+ new Replacement(1, popInstruction, 0)));
+ assertReplacedBranches(m, replacements);
}
/**
@@ -101,17 +103,15 @@ public void should_filter_unoptimized_safe_call_chain() {
final Label label2 = new Label();
m.visitJumpInsn(Opcodes.IFNULL, label1);
- final AbstractInsnNode i1 = m.instructions.getLast();
+ final AbstractInsnNode ifNullInstruction1 = m.instructions.getLast();
m.visitVarInsn(Opcodes.ALOAD, 0);
m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "A", "getB", "()LB;", false);
m.visitVarInsn(Opcodes.ASTORE, 1);
m.visitVarInsn(Opcodes.ALOAD, 1);
m.visitJumpInsn(Opcodes.IFNULL, label1);
- final AbstractInsnNode i2 = m.instructions.getLast();
+ final AbstractInsnNode ifNullInstruction2 = m.instructions.getLast();
m.visitVarInsn(Opcodes.ALOAD, 1);
- final HashSet r = new HashSet();
- r.add(m.instructions.getLast());
m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "B", "getC",
"()Ljava/lang/String;", false);
@@ -119,16 +119,20 @@ public void should_filter_unoptimized_safe_call_chain() {
m.visitLabel(label1);
m.visitInsn(Opcodes.ACONST_NULL);
- r.add(m.instructions.getLast());
+ final AbstractInsnNode aconstNullInstruction = m.instructions.getLast();
m.visitLabel(label2);
filter.filter(m, context, output);
assertIgnored();
- final HashMap> expected = new HashMap>();
- expected.put(i1, r);
- expected.put(i2, r);
- assertReplacedBranches(expected);
+ final HashMap> replacements = new HashMap>();
+ replacements.put(ifNullInstruction1, Arrays.asList( //
+ new Replacement(0, ifNullInstruction2, 0),
+ new Replacement(1, aconstNullInstruction, 0)));
+ replacements.put(ifNullInstruction2, Arrays.asList( //
+ new Replacement(0, ifNullInstruction2, 0),
+ new Replacement(1, aconstNullInstruction, 0)));
+ assertReplacedBranches(m, replacements);
}
}
diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinWhenFilterTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinWhenFilterTest.java
index 42644ff522..1d87f1d28f 100644
--- a/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinWhenFilterTest.java
+++ b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinWhenFilterTest.java
@@ -12,8 +12,7 @@
*******************************************************************************/
package org.jacoco.core.internal.analysis.filter;
-import java.util.HashSet;
-import java.util.Set;
+import java.util.ArrayList;
import org.jacoco.core.internal.instr.InstrSupport;
import org.junit.Test;
@@ -32,6 +31,8 @@ public class KotlinWhenFilterTest extends FilterTestBase {
private final MethodNode m = new MethodNode(InstrSupport.ASM_API_VERSION, 0,
"name", "()V", null, null);
+ private final ArrayList replacements = new ArrayList();
+
@Test
public void should_filter_implicit_else() {
final Label label = new Label();
@@ -96,11 +97,10 @@ public void should_filter_implicit_default() {
m.visitTableSwitchInsn(0, 0, caseDefault, case1);
final AbstractInsnNode switchNode = m.instructions.getLast();
- final Set newTargets = new HashSet();
+ replacements.add(new Replacement(0, switchNode, 1));
m.visitLabel(case1);
m.visitInsn(Opcodes.ICONST_1);
- newTargets.add(m.instructions.getLast());
m.visitJumpInsn(Opcodes.GOTO, after);
final Range range1 = new Range();
@@ -118,7 +118,7 @@ public void should_filter_implicit_default() {
filter.filter(m, context, output);
assertIgnored(range1);
- assertReplacedBranches(switchNode, newTargets);
+ assertReplacedBranches(m, switchNode, replacements);
}
/**
@@ -135,7 +135,6 @@ public void should_filter_implicit_default() {
public void should_filter_when_by_nullable_enum_with_null_case_and_without_else() {
final Range range1 = new Range();
final Range range2 = new Range();
- final HashSet newTargets = new HashSet();
final MethodNode m = new MethodNode(InstrSupport.ASM_API_VERSION, 0,
"example", "(LE;)Ljava/lang/String;", null, null);
final Label l1 = new Label();
@@ -162,23 +161,27 @@ public void should_filter_when_by_nullable_enum_with_null_case_and_without_else(
m.visitInsn(Opcodes.IALOAD);
m.visitLabel(l2);
range1.toInclusive = m.instructions.getLast();
- m.visitTableSwitchInsn(-1, 2, caseElse, caseNull, caseElse, caseA,
- caseB);
+ m.visitTableSwitchInsn(-1, 2, //
+ caseElse, // branch 0
+ caseNull, // branch 1
+ caseElse, // branch 0
+ caseA, // branch 2
+ caseB); // branch 3
final AbstractInsnNode switchNode = m.instructions.getLast();
+ replacements.add(new Replacement(0, switchNode, 1));
+ replacements.add(new Replacement(1, switchNode, 2));
+ replacements.add(new Replacement(2, switchNode, 3));
m.visitLabel(caseA);
m.visitLdcInsn("a");
- newTargets.add(m.instructions.getLast());
m.visitJumpInsn(Opcodes.GOTO, after);
m.visitLabel(caseB);
m.visitLdcInsn("b");
- newTargets.add(m.instructions.getLast());
m.visitJumpInsn(Opcodes.GOTO, after);
m.visitLabel(caseNull);
m.visitLdcInsn("null");
- newTargets.add(m.instructions.getLast());
m.visitJumpInsn(Opcodes.GOTO, after);
m.visitLabel(caseElse);
@@ -196,7 +199,7 @@ public void should_filter_when_by_nullable_enum_with_null_case_and_without_else(
filter.filter(m, context, output);
assertIgnored(range1, range2);
- assertReplacedBranches(switchNode, newTargets);
+ assertReplacedBranches(m, switchNode, replacements);
}
/**
diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinWhenStringFilterTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinWhenStringFilterTest.java
index dbc5578833..0cd7fd9a41 100644
--- a/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinWhenStringFilterTest.java
+++ b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinWhenStringFilterTest.java
@@ -12,8 +12,7 @@
*******************************************************************************/
package org.jacoco.core.internal.analysis.filter;
-import java.util.HashSet;
-import java.util.Set;
+import java.util.ArrayList;
import org.jacoco.core.internal.instr.InstrSupport;
import org.junit.Test;
@@ -29,10 +28,10 @@ public class KotlinWhenStringFilterTest extends FilterTestBase {
private final IFilter filter = new KotlinWhenStringFilter();
+ private final ArrayList replacements = new ArrayList();
+
@Test
public void should_filter() {
- final Set expectedNewTargets = new HashSet();
-
final MethodNode m = new MethodNode(InstrSupport.ASM_API_VERSION, 0,
"name", "()V", null, null);
@@ -55,6 +54,7 @@ public void should_filter() {
m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/String", "hashCode",
"()I", false);
m.visitTableSwitchInsn(97, 98, defaultCase, h1, h2);
+ replacements.add(new Replacement(0, m.instructions.getLast(), 0));
// case "a"
m.visitLabel(h1);
@@ -66,6 +66,7 @@ public void should_filter() {
"(Ljava/lang/Object;)Z", false);
m.visitJumpInsn(Opcodes.IFEQ, sameHash);
m.visitJumpInsn(Opcodes.GOTO, case1);
+ replacements.add(new Replacement(1, m.instructions.getLast(), 0));
// case "\u0000a"
m.visitLabel(sameHash);
@@ -74,7 +75,9 @@ public void should_filter() {
m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/String", "equals",
"(Ljava/lang/Object;)Z", false);
m.visitJumpInsn(Opcodes.IFEQ, defaultCase);
+ replacements.add(new Replacement(0, m.instructions.getLast(), 1));
m.visitJumpInsn(Opcodes.GOTO, case2);
+ replacements.add(new Replacement(2, m.instructions.getLast(), 0));
// case "b"
m.visitLabel(h2);
@@ -83,26 +86,24 @@ public void should_filter() {
m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/String", "equals",
"(Ljava/lang/Object;)Z", false);
m.visitJumpInsn(Opcodes.IFEQ, defaultCase);
+ replacements.add(new Replacement(0, m.instructions.getLast(), 1));
m.visitJumpInsn(Opcodes.GOTO, case3);
final AbstractInsnNode expectedToInclusive = m.instructions.getLast();
+ replacements.add(new Replacement(3, m.instructions.getLast(), 0));
m.visitLabel(case1);
m.visitInsn(Opcodes.RETURN);
- expectedNewTargets.add(m.instructions.getLast());
m.visitLabel(case2);
m.visitInsn(Opcodes.RETURN);
- expectedNewTargets.add(m.instructions.getLast());
m.visitLabel(case3);
m.visitInsn(Opcodes.RETURN);
- expectedNewTargets.add(m.instructions.getLast());
m.visitLabel(defaultCase);
m.visitInsn(Opcodes.RETURN);
- expectedNewTargets.add(m.instructions.getLast());
filter.filter(m, context, output);
- assertReplacedBranches(expectedFromInclusive.getPrevious(),
- expectedNewTargets);
+ assertReplacedBranches(m, expectedFromInclusive.getPrevious(),
+ replacements);
assertIgnored(new Range(expectedFromInclusive, expectedToInclusive));
}
@@ -119,8 +120,6 @@ public void should_filter() {
*/
@Test
public void should_filter_when_biggest_hashCode_first() {
- final Set expectedNewTargets = new HashSet();
-
final MethodNode m = new MethodNode(InstrSupport.ASM_API_VERSION, 0,
"example", "(Ljava/lang/String;)V", null, null);
@@ -136,6 +135,7 @@ public void should_filter_when_biggest_hashCode_first() {
m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/String", "hashCode",
"()I", false);
m.visitTableSwitchInsn(97, 98, defaultCase, h1, h2);
+ replacements.add(new Replacement(0, m.instructions.getLast(), 0));
m.visitLabel(h1);
final AbstractInsnNode expectedFromInclusive = m.instructions.getLast();
@@ -145,6 +145,7 @@ public void should_filter_when_biggest_hashCode_first() {
"(Ljava/lang/Object;)Z", false);
m.visitJumpInsn(Opcodes.IFEQ, sameHash);
m.visitJumpInsn(Opcodes.GOTO, case2);
+ replacements.add(new Replacement(1, m.instructions.getLast(), 0));
m.visitLabel(sameHash);
m.visitVarInsn(Opcodes.ALOAD, 2);
@@ -152,7 +153,9 @@ public void should_filter_when_biggest_hashCode_first() {
m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/String", "equals",
"(Ljava/lang/Object;)Z", false);
m.visitJumpInsn(Opcodes.IFEQ, defaultCase);
+ replacements.add(new Replacement(0, m.instructions.getLast(), 1));
m.visitJumpInsn(Opcodes.GOTO, case3);
+ replacements.add(new Replacement(2, m.instructions.getLast(), 0));
m.visitLabel(h2);
m.visitVarInsn(Opcodes.ALOAD, 2);
@@ -161,25 +164,23 @@ public void should_filter_when_biggest_hashCode_first() {
"(Ljava/lang/Object;)Z", false);
m.visitJumpInsn(Opcodes.IFEQ, defaultCase);
final AbstractInsnNode expectedToInclusive = m.instructions.getLast();
+ replacements.add(new Replacement(0, m.instructions.getLast(), 1));
+ replacements.add(new Replacement(3, m.instructions.getLast(), 0));
m.visitLabel(case1);
m.visitInsn(Opcodes.RETURN);
- expectedNewTargets.add(m.instructions.getLast());
m.visitLabel(case2);
m.visitInsn(Opcodes.RETURN);
- expectedNewTargets.add(m.instructions.getLast());
m.visitLabel(case3);
m.visitInsn(Opcodes.RETURN);
- expectedNewTargets.add(m.instructions.getLast());
m.visitLabel(defaultCase);
m.visitInsn(Opcodes.RETURN);
- expectedNewTargets.add(m.instructions.getLast());
filter.filter(m, context, output);
assertIgnored(new Range(expectedFromInclusive, expectedToInclusive));
- assertReplacedBranches(expectedFromInclusive.getPrevious(),
- expectedNewTargets);
+ assertReplacedBranches(m, expectedFromInclusive.getPrevious(),
+ replacements);
}
@Test
diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/ReplacementsTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/ReplacementsTest.java
new file mode 100644
index 0000000000..6828771b37
--- /dev/null
+++ b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/ReplacementsTest.java
@@ -0,0 +1,131 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2025 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 static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.tree.InsnNode;
+import org.objectweb.asm.tree.LabelNode;
+import org.objectweb.asm.tree.LookupSwitchInsnNode;
+import org.objectweb.asm.tree.TableSwitchInsnNode;
+
+/**
+ * Unit test for {@link Replacements}.
+ */
+public class ReplacementsTest {
+
+ @Test
+ public void should_accumulate() {
+ final Replacements replacements = new Replacements();
+ final InsnNode target1 = new InsnNode(Opcodes.NOP);
+ final InsnNode target2 = new InsnNode(Opcodes.NOP);
+ final InsnNode fromInstruction = new InsnNode(Opcodes.NOP);
+
+ replacements.add(target1, fromInstruction, 0);
+ replacements.add(target2, fromInstruction, 1);
+ ArrayList> expected = new ArrayList>();
+ expected.add(Collections.singletonList(
+ new Replacements.InstructionBranch(fromInstruction, 0)));
+ expected.add(Collections.singletonList(
+ new Replacements.InstructionBranch(fromInstruction, 1)));
+ assertEquals(expected, replacements.values());
+
+ expected = new ArrayList>();
+ expected.add(Arrays.asList(
+ new Replacements.InstructionBranch(fromInstruction, 0),
+ new Replacements.InstructionBranch(fromInstruction, 2)));
+ expected.add(Collections.singletonList(
+ new Replacements.InstructionBranch(fromInstruction, 1)));
+ replacements.add(target1, fromInstruction, 2);
+ assertEquals(expected, replacements.values());
+ }
+
+ @Test
+ public void should_ignore_default_branch_of_LookupSwitch_instruction() {
+ final LabelNode defaultLabel = new LabelNode();
+ final LabelNode caseA = new LabelNode();
+ final LabelNode caseB = new LabelNode();
+ final LookupSwitchInsnNode switchNode = new LookupSwitchInsnNode(
+ defaultLabel, // branch 0
+ new int[] { 0, 1, 2, 3 }, new LabelNode[] { //
+ caseA, // branch 1
+ defaultLabel, // branch 0
+ caseA, // branch 1
+ caseB // branch 2
+ });
+ final ArrayList> expected = new ArrayList>();
+ expected.add(Collections.singletonList(
+ new Replacements.InstructionBranch(switchNode, 1)));
+ expected.add(Collections.singletonList(
+ new Replacements.InstructionBranch(switchNode, 2)));
+ assertEquals(expected,
+ Replacements.ignoreDefaultBranch(switchNode).values());
+ }
+
+ @Test
+ public void should_ignore_default_branch_of_TableSwitch_instruction() {
+ final LabelNode defaultLabel = new LabelNode();
+ final LabelNode caseA = new LabelNode();
+ final LabelNode caseB = new LabelNode();
+ final TableSwitchInsnNode switchNode = new TableSwitchInsnNode(0, 3,
+ defaultLabel, // branch 0
+ caseA, // branch 1
+ defaultLabel, // branch 0
+ caseA, // branch 1
+ caseB // branch 2
+ );
+ final ArrayList> expected = new ArrayList>();
+ expected.add(Collections.singletonList(
+ new Replacements.InstructionBranch(switchNode, 1)));
+ expected.add(Collections.singletonList(
+ new Replacements.InstructionBranch(switchNode, 2)));
+ assertEquals(expected,
+ Replacements.ignoreDefaultBranch(switchNode).values());
+ }
+
+ private static void assertEquals(final Iterable expected,
+ final Iterable actual) {
+ final Iterator e = expected.iterator();
+ final Iterator a = actual.iterator();
+ while (e.hasNext() && a.hasNext()) {
+ Assert.assertEquals(e.next(), a.next());
+ }
+ Assert.assertEquals(e.hasNext(), a.hasNext());
+ }
+
+ @Test
+ public void test_InstructionBranch_equals_and_hashCode() {
+ final InsnNode i1 = new InsnNode(Opcodes.NOP);
+ final InsnNode i2 = new InsnNode(Opcodes.NOP);
+ final Replacements.InstructionBranch ib = new Replacements.InstructionBranch(
+ i1, 0);
+ assertFalse(ib.equals(null));
+ assertFalse(ib.equals(new Object()));
+ assertFalse(ib.equals(new Replacements.InstructionBranch(i1, 1)));
+ assertFalse(ib.equals(new Replacements.InstructionBranch(i2, 0)));
+ assertTrue(ib.equals(new Replacements.InstructionBranch(i1, 0)));
+ Assert.assertEquals(ib.hashCode(),
+ new Replacements.InstructionBranch(i1, 0).hashCode());
+ }
+
+}
diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/StringSwitchFilterTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/StringSwitchFilterTest.java
index bfcf7f413b..f241d6c3c5 100644
--- a/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/StringSwitchFilterTest.java
+++ b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/StringSwitchFilterTest.java
@@ -12,14 +12,12 @@
*******************************************************************************/
package org.jacoco.core.internal.analysis.filter;
-import java.util.HashSet;
-import java.util.Set;
+import java.util.ArrayList;
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;
/**
@@ -29,11 +27,11 @@ public class StringSwitchFilterTest extends FilterTestBase {
private final IFilter filter = new StringSwitchFilter();
+ private final ArrayList replacements = new ArrayList();
+
@Test
public void should_filter() {
final Range range = new Range();
- final Set expectedNewTargets = new HashSet();
-
final MethodNode m = new MethodNode(InstrSupport.ASM_API_VERSION, 0,
"name", "()V", null, null);
@@ -56,6 +54,7 @@ public void should_filter() {
"()I", false);
range.fromInclusive = m.instructions.getLast();
m.visitTableSwitchInsn(97, 98, caseDefault, h1, h2);
+ replacements.add(new Replacement(0, m.instructions.getLast(), 0));
m.visitLabel(h1);
@@ -65,6 +64,7 @@ public void should_filter() {
"(Ljava/lang/Object;)Z", false);
// if equal "a", then goto its case
m.visitJumpInsn(Opcodes.IFNE, case1);
+ replacements.add(new Replacement(1, m.instructions.getLast(), 1));
m.visitVarInsn(Opcodes.ALOAD, 2);
m.visitLdcInsn("\0a");
@@ -72,9 +72,11 @@ public void should_filter() {
"(Ljava/lang/Object;)Z", false);
// if equal "\0a", then goto its case
m.visitJumpInsn(Opcodes.IFNE, case2);
+ replacements.add(new Replacement(2, m.instructions.getLast(), 1));
// goto default case
m.visitJumpInsn(Opcodes.GOTO, caseDefault);
+ replacements.add(new Replacement(0, m.instructions.getLast(), 0));
m.visitLabel(h2);
@@ -84,36 +86,32 @@ public void should_filter() {
"(Ljava/lang/Object;)Z", false);
// if equal "b", then goto its case
m.visitJumpInsn(Opcodes.IFNE, case3);
+ replacements.add(new Replacement(3, m.instructions.getLast(), 1));
// goto default case
m.visitJumpInsn(Opcodes.GOTO, caseDefault);
range.toInclusive = m.instructions.getLast();
+ replacements.add(new Replacement(0, m.instructions.getLast(), 0));
m.visitLabel(case1);
m.visitInsn(Opcodes.RETURN);
- expectedNewTargets.add(m.instructions.getLast());
m.visitLabel(case2);
m.visitInsn(Opcodes.RETURN);
- expectedNewTargets.add(m.instructions.getLast());
m.visitLabel(case3);
m.visitInsn(Opcodes.RETURN);
- expectedNewTargets.add(m.instructions.getLast());
m.visitLabel(caseDefault);
m.visitInsn(Opcodes.RETURN);
- expectedNewTargets.add(m.instructions.getLast());
filter.filter(m, context, output);
assertIgnored(range);
- assertReplacedBranches(range.fromInclusive.getPrevious(),
- expectedNewTargets);
+ assertReplacedBranches(m, range.fromInclusive.getPrevious(),
+ replacements);
}
@Test
public void should_filter_when_default_is_first() {
final Range range = new Range();
- final Set expectedNewTargets = new HashSet();
-
final MethodNode m = new MethodNode(InstrSupport.ASM_API_VERSION, 0,
"name", "()V", null, null);
@@ -134,6 +132,7 @@ public void should_filter_when_default_is_first() {
range.fromInclusive = m.instructions.getLast();
m.visitLookupSwitchInsn(caseDefault, new int[] { 97 },
new Label[] { h1 });
+ replacements.add(new Replacement(0, m.instructions.getLast(), 0));
m.visitLabel(h1);
m.visitVarInsn(Opcodes.ALOAD, 2);
@@ -143,19 +142,19 @@ public void should_filter_when_default_is_first() {
// if equal "a", then goto its case
m.visitJumpInsn(Opcodes.IFNE, case1);
range.toInclusive = m.instructions.getLast();
+ replacements.add(new Replacement(1, m.instructions.getLast(), 1));
+ replacements.add(new Replacement(0, m.instructions.getLast(), 0));
m.visitLabel(caseDefault);
m.visitInsn(Opcodes.RETURN);
- expectedNewTargets.add(m.instructions.getLast());
m.visitLabel(case1);
m.visitInsn(Opcodes.RETURN);
- expectedNewTargets.add(m.instructions.getLast());
filter.filter(m, context, output);
assertIgnored(range);
- assertReplacedBranches(range.fromInclusive.getPrevious(),
- expectedNewTargets);
+ assertReplacedBranches(m, range.fromInclusive.getPrevious(),
+ replacements);
}
/**
@@ -175,8 +174,6 @@ public void should_filter_when_default_is_first() {
@Test
public void should_filter_Kotlin_1_5() {
final Range range = new Range();
- final Set expectedNewTargets = new HashSet();
-
final MethodNode m = new MethodNode(InstrSupport.ASM_API_VERSION, 0,
"example", "()V", null, null);
@@ -197,6 +194,7 @@ public void should_filter_Kotlin_1_5() {
m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/String", "hashCode",
"()I", false);
m.visitTableSwitchInsn(97, 99, defaultCase, h1, h2, h3);
+ replacements.add(new Replacement(0, m.instructions.getLast(), 0));
m.visitLabel(h1);
m.visitVarInsn(Opcodes.ALOAD, 1);
@@ -204,14 +202,17 @@ public void should_filter_Kotlin_1_5() {
m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/String", "equals",
"(Ljava/lang/Object;)Z", false);
m.visitJumpInsn(Opcodes.IFNE, case1);
+ replacements.add(new Replacement(1, m.instructions.getLast(), 1));
m.visitVarInsn(Opcodes.ALOAD, 1);
m.visitLdcInsn("\u0000a");
m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/String", "equals",
"(Ljava/lang/Object;)Z", false);
m.visitJumpInsn(Opcodes.IFNE, case2);
+ replacements.add(new Replacement(2, m.instructions.getLast(), 1));
m.visitJumpInsn(Opcodes.GOTO, defaultCase);
+ replacements.add(new Replacement(0, m.instructions.getLast(), 0));
m.visitLabel(h2);
m.visitVarInsn(Opcodes.ALOAD, 1);
@@ -219,14 +220,17 @@ public void should_filter_Kotlin_1_5() {
m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/String", "equals",
"(Ljava/lang/Object;)Z", false);
m.visitJumpInsn(Opcodes.IFNE, case3);
+ replacements.add(new Replacement(3, m.instructions.getLast(), 1));
m.visitVarInsn(Opcodes.ALOAD, 1);
m.visitLdcInsn("\u0000b");
m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/String", "equals",
"(Ljava/lang/Object;)Z", false);
m.visitJumpInsn(Opcodes.IFNE, case4);
+ replacements.add(new Replacement(4, m.instructions.getLast(), 1));
m.visitJumpInsn(Opcodes.GOTO, defaultCase);
+ replacements.add(new Replacement(0, m.instructions.getLast(), 0));
m.visitLabel(h3);
m.visitVarInsn(Opcodes.ALOAD, 1);
@@ -234,43 +238,39 @@ public void should_filter_Kotlin_1_5() {
m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/String", "equals",
"(Ljava/lang/Object;)Z", false);
m.visitJumpInsn(Opcodes.IFNE, case5);
+ replacements.add(new Replacement(5, m.instructions.getLast(), 1));
m.visitVarInsn(Opcodes.ALOAD, 1);
m.visitLdcInsn("\u0000c");
m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/String", "equals",
"(Ljava/lang/Object;)Z", false);
m.visitJumpInsn(Opcodes.IFNE, case6);
+ replacements.add(new Replacement(6, m.instructions.getLast(), 1));
m.visitJumpInsn(Opcodes.GOTO, defaultCase);
range.toInclusive = m.instructions.getLast();
+ replacements.add(new Replacement(0, m.instructions.getLast(), 0));
m.visitLabel(case1);
m.visitInsn(Opcodes.RETURN);
- expectedNewTargets.add(m.instructions.getLast());
m.visitLabel(case2);
m.visitInsn(Opcodes.RETURN);
- expectedNewTargets.add(m.instructions.getLast());
m.visitLabel(case3);
m.visitInsn(Opcodes.RETURN);
- expectedNewTargets.add(m.instructions.getLast());
m.visitLabel(case4);
m.visitInsn(Opcodes.RETURN);
- expectedNewTargets.add(m.instructions.getLast());
m.visitLabel(case5);
m.visitInsn(Opcodes.RETURN);
- expectedNewTargets.add(m.instructions.getLast());
m.visitLabel(case6);
m.visitInsn(Opcodes.RETURN);
- expectedNewTargets.add(m.instructions.getLast());
m.visitLabel(defaultCase);
m.visitInsn(Opcodes.RETURN);
- expectedNewTargets.add(m.instructions.getLast());
filter.filter(m, context, output);
assertIgnored(range);
- assertReplacedBranches(range.fromInclusive.getPrevious(),
- expectedNewTargets);
+ assertReplacedBranches(m, range.fromInclusive.getPrevious(),
+ replacements);
}
/**
@@ -286,8 +286,6 @@ public void should_filter_Kotlin_1_5() {
@Test
public void should_filter_Kotlin_nullable_else() {
final Range range = new Range();
- final Set expectedNewTargets = new HashSet();
-
final MethodNode m = new MethodNode(InstrSupport.ASM_API_VERSION, 0,
"example", "(Ljava/lang/String;)Ljava/lang/String;", null,
null);
@@ -305,11 +303,13 @@ public void should_filter_Kotlin_nullable_else() {
m.visitVarInsn(Opcodes.ALOAD, 2);
range.fromInclusive = m.instructions.getLast();
m.visitJumpInsn(Opcodes.IFNULL, caseElse);
+ replacements.add(new Replacement(0, m.instructions.getLast(), 1));
m.visitVarInsn(Opcodes.ALOAD, 2);
m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/String", "hashCode",
"()I", false);
m.visitTableSwitchInsn(97, 99, caseElse,
new Label[] { hashA, hashB, hashC });
+ replacements.add(new Replacement(0, m.instructions.getLast(), 0));
m.visitLabel(hashA);
m.visitVarInsn(Opcodes.ALOAD, 2);
@@ -317,7 +317,9 @@ public void should_filter_Kotlin_nullable_else() {
m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/String", "equals",
"(Ljava/lang/Object;)Z", false);
m.visitJumpInsn(Opcodes.IFNE, caseA);
+ replacements.add(new Replacement(1, m.instructions.getLast(), 1));
m.visitJumpInsn(Opcodes.GOTO, caseElse);
+ replacements.add(new Replacement(0, m.instructions.getLast(), 0));
m.visitLabel(hashB);
m.visitVarInsn(Opcodes.ALOAD, 2);
@@ -325,7 +327,9 @@ public void should_filter_Kotlin_nullable_else() {
m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/String", "equals",
"(Ljava/lang/Object;)Z", false);
m.visitJumpInsn(Opcodes.IFNE, caseB);
+ replacements.add(new Replacement(2, m.instructions.getLast(), 1));
m.visitJumpInsn(Opcodes.GOTO, caseElse);
+ replacements.add(new Replacement(0, m.instructions.getLast(), 0));
m.visitLabel(hashC);
m.visitVarInsn(Opcodes.ALOAD, 2);
@@ -333,27 +337,25 @@ public void should_filter_Kotlin_nullable_else() {
m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/String", "equals",
"(Ljava/lang/Object;)Z", false);
m.visitJumpInsn(Opcodes.IFNE, caseC);
+ replacements.add(new Replacement(3, m.instructions.getLast(), 1));
m.visitJumpInsn(Opcodes.GOTO, caseElse);
range.toInclusive = m.instructions.getLast();
+ replacements.add(new Replacement(0, m.instructions.getLast(), 0));
m.visitLabel(caseA);
m.visitLdcInsn("case a");
- expectedNewTargets.add(m.instructions.getLast());
m.visitJumpInsn(Opcodes.GOTO, end);
m.visitLabel(caseB);
m.visitLdcInsn("case b");
- expectedNewTargets.add(m.instructions.getLast());
m.visitJumpInsn(Opcodes.GOTO, end);
m.visitLabel(caseC);
m.visitLdcInsn("case c");
- expectedNewTargets.add(m.instructions.getLast());
m.visitJumpInsn(Opcodes.GOTO, end);
m.visitLabel(caseElse);
m.visitLdcInsn("else");
- expectedNewTargets.add(m.instructions.getLast());
m.visitLabel(end);
m.visitInsn(Opcodes.ARETURN);
@@ -361,8 +363,8 @@ public void should_filter_Kotlin_nullable_else() {
filter.filter(m, context, output);
assertIgnored(range);
- assertReplacedBranches(range.fromInclusive.getPrevious(),
- expectedNewTargets);
+ assertReplacedBranches(m, range.fromInclusive.getPrevious(),
+ replacements);
}
/**
@@ -379,8 +381,6 @@ public void should_filter_Kotlin_nullable_else() {
@Test
public void should_filter_Kotlin_nullable_case() {
final Range range = new Range();
- final Set expectedNewTargets = new HashSet();
-
final MethodNode m = new MethodNode(InstrSupport.ASM_API_VERSION, 0,
"example", "(Ljava/lang/String;)Ljava/lang/String;", null,
null);
@@ -400,10 +400,12 @@ public void should_filter_Kotlin_nullable_case() {
range.fromInclusive = m.instructions.getLast();
m.visitInsn(Opcodes.DUP);
m.visitJumpInsn(Opcodes.IFNULL, caseNull);
+ replacements.add(new Replacement(4, m.instructions.getLast(), 1));
m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/String", "hashCode",
"()I", false);
m.visitTableSwitchInsn(97, 99, caseElse,
new Label[] { hashA, hashB, hashC });
+ replacements.add(new Replacement(0, m.instructions.getLast(), 0));
m.visitLabel(hashA);
m.visitVarInsn(Opcodes.ALOAD, 2);
@@ -411,7 +413,9 @@ public void should_filter_Kotlin_nullable_case() {
m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/String", "equals",
"(Ljava/lang/Object;)Z", false);
m.visitJumpInsn(Opcodes.IFNE, caseA);
+ replacements.add(new Replacement(1, m.instructions.getLast(), 1));
m.visitJumpInsn(Opcodes.GOTO, caseElse);
+ replacements.add(new Replacement(0, m.instructions.getLast(), 0));
m.visitLabel(hashB);
m.visitVarInsn(Opcodes.ALOAD, 2);
@@ -419,7 +423,9 @@ public void should_filter_Kotlin_nullable_case() {
m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/String", "equals",
"(Ljava/lang/Object;)Z", false);
m.visitJumpInsn(Opcodes.IFNE, caseB);
+ replacements.add(new Replacement(2, m.instructions.getLast(), 1));
m.visitJumpInsn(Opcodes.GOTO, caseElse);
+ replacements.add(new Replacement(0, m.instructions.getLast(), 0));
m.visitLabel(hashC);
m.visitVarInsn(Opcodes.ALOAD, 2);
@@ -427,33 +433,30 @@ public void should_filter_Kotlin_nullable_case() {
m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/String", "equals",
"(Ljava/lang/Object;)Z", false);
m.visitJumpInsn(Opcodes.IFNE, caseC);
+ replacements.add(new Replacement(3, m.instructions.getLast(), 1));
m.visitJumpInsn(Opcodes.GOTO, caseElse);
range.toInclusive = m.instructions.getLast();
+ replacements.add(new Replacement(0, m.instructions.getLast(), 0));
m.visitLabel(caseA);
m.visitLdcInsn("case a");
- expectedNewTargets.add(m.instructions.getLast());
m.visitJumpInsn(Opcodes.GOTO, end);
m.visitLabel(caseB);
m.visitLdcInsn("case b");
- expectedNewTargets.add(m.instructions.getLast());
m.visitJumpInsn(Opcodes.GOTO, end);
m.visitLabel(caseC);
m.visitLdcInsn("case c");
- expectedNewTargets.add(m.instructions.getLast());
m.visitJumpInsn(Opcodes.GOTO, end);
m.visitLabel(caseNull);
m.visitInsn(Opcodes.POP);
- expectedNewTargets.add(m.instructions.getLast());
m.visitLdcInsn("null");
m.visitJumpInsn(Opcodes.GOTO, end);
m.visitLabel(caseElse);
m.visitLdcInsn("else");
- expectedNewTargets.add(m.instructions.getLast());
m.visitLabel(end);
m.visitInsn(Opcodes.ARETURN);
@@ -461,8 +464,8 @@ public void should_filter_Kotlin_nullable_case() {
filter.filter(m, context, output);
assertIgnored(range);
- assertReplacedBranches(range.fromInclusive.getPrevious(),
- expectedNewTargets);
+ assertReplacedBranches(m, range.fromInclusive.getPrevious(),
+ replacements);
}
@Test
@@ -481,6 +484,7 @@ public void should_not_filter_empty_lookup_switch() {
filter.filter(m, context, output);
assertIgnored();
+ assertNoReplacedBranches();
}
}
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/Instruction.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/Instruction.java
index 5f9cb79bf8..963864431a 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/Instruction.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/Instruction.java
@@ -16,6 +16,8 @@
import java.util.Collection;
import org.jacoco.core.analysis.ICounter;
+import org.jacoco.core.internal.analysis.filter.Replacements;
+import org.objectweb.asm.tree.AbstractInsnNode;
/**
* Execution status of a single bytecode instruction internally used for
@@ -50,7 +52,7 @@
*
*
*
{@link #merge(Instruction)}
- *
{@link #replaceBranches(Collection)}
+ *
{@link #replaceBranches(Replacements, Mapper)}
*
*/
public class Instruction {
@@ -160,26 +162,42 @@ public Instruction merge(final Instruction other) {
/**
* Creates a copy of this instruction where all outgoing branches are
- * replaced with the given instructions. The coverage status of the new
- * instruction is derived from the status of the given instructions.
+ * replaced. The coverage statuses of the branches of the new instruction
+ * are derived from the statuses of the given branches of the given
+ * instructions.
*
- * @param newBranches
- * new branches to consider
+ * @param replacements
+ * new branches
+ * @param mapper
+ * provides {@link Instruction} corresponding to
+ * {@link Replacements.InstructionBranch#instruction}
* @return new instance with replaced branches
*/
- public Instruction replaceBranches(
- final Collection newBranches) {
+ public Instruction replaceBranches(final Replacements replacements,
+ final Mapper mapper) {
final Instruction result = new Instruction(this.line);
- result.branches = newBranches.size();
- int idx = 0;
- for (final Instruction b : newBranches) {
- if (!b.coveredBranches.isEmpty()) {
- result.coveredBranches.set(idx++);
+ int branchIndex = 0;
+ for (final Collection newBranch : replacements
+ .values()) {
+ for (final Replacements.InstructionBranch from : newBranch) {
+ if (mapper.apply(from.instruction).coveredBranches
+ .get(from.branch)) {
+ result.coveredBranches.set(branchIndex);
+ }
}
+ branchIndex++;
}
+ result.branches = branchIndex;
return result;
}
+ /**
+ * {@code java.util.function.Function}
+ */
+ interface Mapper {
+ Instruction apply(AbstractInsnNode node);
+ }
+
/**
* Returns the instruction coverage counter of this instruction. It is
* always 1 instruction which is covered or not.
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/MethodCoverageCalculator.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/MethodCoverageCalculator.java
index 6f4658b2db..4c5fcbef7f 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/MethodCoverageCalculator.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/MethodCoverageCalculator.java
@@ -12,16 +12,15 @@
*******************************************************************************/
package org.jacoco.core.internal.analysis;
-import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.jacoco.core.analysis.ISourceNode;
import org.jacoco.core.internal.analysis.filter.IFilterOutput;
+import org.jacoco.core.internal.analysis.filter.Replacements;
import org.objectweb.asm.tree.AbstractInsnNode;
/**
@@ -48,14 +47,14 @@ class MethodCoverageCalculator implements IFilterOutput {
*/
private final Map merged;
- private final Map> replacements;
+ private final Map replacements;
MethodCoverageCalculator(
final Map instructions) {
this.instructions = instructions;
this.ignored = new HashSet();
this.merged = new HashMap();
- this.replacements = new HashMap>();
+ this.replacements = new HashMap();
}
/**
@@ -105,17 +104,16 @@ private void applyMerges() {
}
private void applyReplacements() {
- for (final Entry> entry : replacements
- .entrySet()) {
- final Set replacements = entry.getValue();
- final List newBranches = new ArrayList(
- replacements.size());
- for (final AbstractInsnNode b : replacements) {
- newBranches.add(instructions.get(b));
+ final Instruction.Mapper mapper = new Instruction.Mapper() {
+ public Instruction apply(final AbstractInsnNode node) {
+ return instructions.get(node);
}
+ };
+ for (final Entry entry : replacements
+ .entrySet()) {
final AbstractInsnNode node = entry.getKey();
- instructions.put(node,
- instructions.get(node).replaceBranches(newBranches));
+ instructions.put(node, instructions.get(node)
+ .replaceBranches(entry.getValue(), mapper));
}
}
@@ -171,8 +169,8 @@ public void merge(AbstractInsnNode i1, AbstractInsnNode i2) {
}
public void replaceBranches(final AbstractInsnNode source,
- final Set newTargets) {
- replacements.put(source, newTargets);
+ final Replacements replacements) {
+ this.replacements.put(source, replacements);
}
}
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/ExhaustiveSwitchFilter.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/ExhaustiveSwitchFilter.java
index eaa2d58c1d..881284d3b1 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/ExhaustiveSwitchFilter.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/ExhaustiveSwitchFilter.java
@@ -12,9 +12,6 @@
*******************************************************************************/
package org.jacoco.core.internal.analysis.filter;
-import java.util.HashSet;
-import java.util.List;
-
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.LabelNode;
@@ -46,13 +43,10 @@ private static class Matcher extends AbstractMatcher {
public void match(final AbstractInsnNode start, final int line,
final IFilterOutput output) {
final LabelNode dflt;
- final List labels;
if (start.getOpcode() == Opcodes.LOOKUPSWITCH) {
dflt = ((LookupSwitchInsnNode) start).dflt;
- labels = ((LookupSwitchInsnNode) start).labels;
} else if (start.getOpcode() == Opcodes.TABLESWITCH) {
dflt = ((TableSwitchInsnNode) start).dflt;
- labels = ((TableSwitchInsnNode) start).labels;
} else {
return;
}
@@ -93,11 +87,8 @@ public void match(final AbstractInsnNode start, final int line,
return;
}
output.ignore(dflt, cursor);
- final HashSet replacements = new HashSet();
- for (final AbstractInsnNode label : labels) {
- replacements.add(skipNonOpcodes(label));
- }
- output.replaceBranches(start, replacements);
+ output.replaceBranches(start,
+ Replacements.ignoreDefaultBranch(start));
}
private static AbstractInsnNode skipToLineNumberOrInstruction(
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/IFilterOutput.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/IFilterOutput.java
index 39066e6c0d..effd2ce965 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/IFilterOutput.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/IFilterOutput.java
@@ -12,8 +12,6 @@
*******************************************************************************/
package org.jacoco.core.internal.analysis.filter;
-import java.util.Set;
-
import org.objectweb.asm.tree.AbstractInsnNode;
/**
@@ -50,10 +48,9 @@ public interface IFilterOutput {
*
* @param source
* instruction which branches should be replaced
- * @param newTargets
- * new targets of branches
+ * @param replacements
+ * new branches
*/
- void replaceBranches(AbstractInsnNode source,
- Set newTargets);
+ void replaceBranches(AbstractInsnNode source, Replacements replacements);
}
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinSafeCallOperatorFilter.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinSafeCallOperatorFilter.java
index 50b7173467..e285687ed1 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinSafeCallOperatorFilter.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinSafeCallOperatorFilter.java
@@ -15,7 +15,6 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
-import java.util.HashSet;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
@@ -36,12 +35,15 @@ public void filter(final MethodNode methodNode,
if (chain.size() == 1) {
continue;
}
- final JumpInsnNode lastJump = chain.get(chain.size() - 1);
- final HashSet newTargets = new HashSet();
- newTargets.add(AbstractMatcher.skipNonOpcodes(lastJump.getNext()));
- newTargets.add(AbstractMatcher.skipNonOpcodes(lastJump.label));
- for (final AbstractInsnNode i : chain) {
- output.replaceBranches(i, newTargets);
+ final JumpInsnNode lastIfNullInstruction = chain
+ .get(chain.size() - 1);
+ final AbstractInsnNode nullTarget = AbstractMatcher
+ .skipNonOpcodes(lastIfNullInstruction.label);
+ final Replacements replacements = new Replacements();
+ replacements.add(lastIfNullInstruction, lastIfNullInstruction, 0);
+ replacements.add(nullTarget, nullTarget, 0);
+ for (final AbstractInsnNode ifNullInstruction : chain) {
+ output.replaceBranches(ifNullInstruction, replacements);
}
}
}
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinWhenFilter.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinWhenFilter.java
index d082b93484..582a5fdaf1 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinWhenFilter.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinWhenFilter.java
@@ -12,10 +12,6 @@
*******************************************************************************/
package org.jacoco.core.internal.analysis.filter;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.FieldInsnNode;
@@ -63,7 +59,8 @@ void match(final AbstractInsnNode start, final IFilterOutput output) {
return;
} else if (getDefaultLabel(i) == start) {
- ignoreDefaultBranch(i, output);
+ output.replaceBranches(i,
+ Replacements.ignoreDefaultBranch(i));
output.ignore(start, cursor);
return;
@@ -116,22 +113,4 @@ private static LabelNode getDefaultLabel(final AbstractInsnNode i) {
}
}
- private static void ignoreDefaultBranch(final AbstractInsnNode switchNode,
- final IFilterOutput output) {
- final List labels;
- if (switchNode.getOpcode() == Opcodes.LOOKUPSWITCH) {
- labels = ((LookupSwitchInsnNode) switchNode).labels;
- } else {
- labels = ((TableSwitchInsnNode) switchNode).labels;
- }
- final LabelNode defaultLabel = getDefaultLabel(switchNode);
- final Set newTargets = new HashSet();
- for (final LabelNode label : labels) {
- if (label != defaultLabel) {
- newTargets.add(AbstractMatcher.skipNonOpcodes(label));
- }
- }
- output.replaceBranches(switchNode, newTargets);
- }
-
}
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinWhenStringFilter.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinWhenStringFilter.java
index f488c28d02..2296948335 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinWhenStringFilter.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinWhenStringFilter.java
@@ -12,9 +12,6 @@
*******************************************************************************/
package org.jacoco.core.internal.analysis.filter;
-import java.util.HashSet;
-import java.util.Set;
-
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.JumpInsnNode;
@@ -71,8 +68,8 @@ public void match(final AbstractInsnNode start,
return;
}
- final Set replacements = new HashSet();
- replacements.add(skipNonOpcodes(defaultLabel));
+ final Replacements replacements = new Replacements();
+ replacements.add(defaultLabel, s, 0);
for (int i = 1; i <= hashCodes; i++) {
while (true) {
@@ -88,15 +85,17 @@ public void match(final AbstractInsnNode start,
return;
} else if (cursor.getOpcode() == Opcodes.GOTO) {
// jump to case body
- replacements.add(
- skipNonOpcodes(((JumpInsnNode) cursor).label));
+ replacements.add(((JumpInsnNode) cursor).label, cursor,
+ 0);
if (jump.label == defaultLabel) {
// end of comparisons for same hashCode
+ replacements.add(defaultLabel, jump, 1);
break;
}
} else if (i == hashCodes && jump.label == defaultLabel) {
// case body
- replacements.add(cursor);
+ replacements.add(defaultLabel, jump, 1);
+ replacements.add(cursor, jump, 0);
cursor = jump;
break;
} else {
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/Replacements.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/Replacements.java
new file mode 100644
index 0000000000..0a7a03c386
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/Replacements.java
@@ -0,0 +1,178 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2025 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 java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.List;
+
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.tree.AbstractInsnNode;
+import org.objectweb.asm.tree.LabelNode;
+import org.objectweb.asm.tree.LookupSwitchInsnNode;
+import org.objectweb.asm.tree.TableSwitchInsnNode;
+
+/**
+ * Utility for creating an argument for
+ * {@link IFilterOutput#replaceBranches(AbstractInsnNode, Replacements)} with
+ * information about how to compute the coverage status of branches of
+ * instruction from the coverage status of branches of other instructions.
+ */
+public final class Replacements {
+
+ private final LinkedHashMap> newBranches = new LinkedHashMap>();
+
+ /**
+ * Adds branch which has a given target and which should be considered as
+ * covered when a branch with a given index of a given instruction is
+ * covered.
+ *
+ * The branch index should be specified in accordance with the ones assigned
+ * by {@link org.jacoco.core.internal.analysis.MethodAnalyzer} to a given
+ * instruction:
+ *
+ *
+ *
for {@link org.objectweb.asm.tree.TableSwitchInsnNode} (and similarly
+ * for {@link org.objectweb.asm.tree.LookupSwitchInsnNode})
+ *
+ *
the branch index corresponds to the indexes in the list of unique
+ * labels among {@link org.objectweb.asm.tree.TableSwitchInsnNode#dflt} and
+ * {@link org.objectweb.asm.tree.TableSwitchInsnNode#labels}
+ *
there are as many branches as unique labels
+ *
branch 0 corresponds to continuation of execution at
+ * {@link org.objectweb.asm.tree.TableSwitchInsnNode#dflt}
+ *
+ *
+ *
+ *
for {@link org.objectweb.asm.tree.JumpInsnNode} with
+ * {@link org.objectweb.asm.Opcodes#GOTO} there is only branch 0 that
+ * corresponds to continuation of execution at
+ * {@link org.objectweb.asm.tree.JumpInsnNode#label}
+ *
+ *
for other {@link org.objectweb.asm.tree.JumpInsnNode} there are two
+ * branches
+ *
+ *
branch 1 corresponds to continuation of execution at
+ * {@link org.objectweb.asm.tree.JumpInsnNode#label}
+ *
branch 0 corresponds to continuation of execution at
+ * {@link AbstractInsnNode#getNext()}
+ *
+ *
+ *
+ *
for instructions with {@link org.objectweb.asm.Opcodes#RETURN} and
+ * {@link org.objectweb.asm.Opcodes#ATHROW} there is only branch 0 that
+ * corresponds to exit from the method
+ *
+ *
there are no branches for instructions whose
+ * {@link AbstractInsnNode#getOpcode()} is -1
+ *
+ *
for other instructions there is only branch 0 that corresponds to
+ * continuation of execution at {@link AbstractInsnNode#getNext()}
+ *
+ *
+ * @param target
+ * instruction uniquely identifying new branch, e.g. its target
+ * @param instruction
+ * instruction whose branch execution status should be used
+ * @param branchIndex
+ * index of branch whose execution status should be used
+ */
+ public void add(final AbstractInsnNode target,
+ final AbstractInsnNode instruction, final int branchIndex) {
+ Collection from = newBranches.get(target);
+ if (from == null) {
+ from = new ArrayList();
+ newBranches.put(target, from);
+ }
+ from.add(new InstructionBranch(instruction, branchIndex));
+ }
+
+ /**
+ * @return the accumulated information in the order of
+ * {@link #add(AbstractInsnNode, AbstractInsnNode, int) additions}
+ */
+ public Iterable> values() {
+ return newBranches.values();
+ }
+
+ /**
+ * @return information about how to compute coverage status of branches of a
+ * given {@link TableSwitchInsnNode} or {@link LookupSwitchInsnNode}
+ * in order to ignore its {@link TableSwitchInsnNode#dflt} or
+ * {@link LookupSwitchInsnNode#dflt}
+ */
+ static Replacements ignoreDefaultBranch(final AbstractInsnNode switchNode) {
+ final List labels;
+ final LabelNode defaultLabel;
+ if (switchNode.getOpcode() == Opcodes.LOOKUPSWITCH) {
+ final LookupSwitchInsnNode s = (LookupSwitchInsnNode) switchNode;
+ labels = s.labels;
+ defaultLabel = s.dflt;
+ } else {
+ final TableSwitchInsnNode s = (TableSwitchInsnNode) switchNode;
+ labels = s.labels;
+ defaultLabel = s.dflt;
+ }
+ final Replacements replacements = new Replacements();
+ int branchIndex = 0;
+ for (final LabelNode label : labels) {
+ if (label != defaultLabel
+ && replacements.newBranches.get(label) == null) {
+ branchIndex++;
+ replacements.add(label, switchNode, branchIndex);
+ }
+ }
+ return replacements;
+ }
+
+ /**
+ * {@link #instruction} and index of its {@link #branch}.
+ */
+ public static final class InstructionBranch {
+ /** Instruction. */
+ public final AbstractInsnNode instruction;
+ /** Branch index. */
+ public final int branch;
+
+ /**
+ * Creates a new {@link InstructionBranch}.
+ *
+ * @param instruction
+ * instruction
+ * @param branch
+ * branch index
+ */
+ public InstructionBranch(final AbstractInsnNode instruction,
+ final int branch) {
+ this.instruction = instruction;
+ this.branch = branch;
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final InstructionBranch other = (InstructionBranch) o;
+ return this.instruction.equals(other.instruction)
+ && this.branch == other.branch;
+ }
+
+ @Override
+ public int hashCode() {
+ return instruction.hashCode() * 31 + branch;
+ }
+ }
+
+}
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/StringSwitchFilter.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/StringSwitchFilter.java
index 427790bc85..17d62a802c 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/StringSwitchFilter.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/StringSwitchFilter.java
@@ -12,9 +12,6 @@
*******************************************************************************/
package org.jacoco.core.internal.analysis.filter;
-import java.util.HashSet;
-import java.util.Set;
-
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.JumpInsnNode;
@@ -89,8 +86,8 @@ public void match(final AbstractInsnNode start,
return;
}
- final Set replacements = new HashSet();
- replacements.add(skipNonOpcodes(defaultLabel));
+ final Replacements replacements = new Replacements();
+ replacements.add(defaultLabel, s, 0);
for (int i = 0; i < hashCodes; i++) {
while (true) {
@@ -104,22 +101,23 @@ public void match(final AbstractInsnNode start,
return;
}
- replacements
- .add(skipNonOpcodes(((JumpInsnNode) cursor).label));
+ replacements.add(((JumpInsnNode) cursor).label, cursor, 1);
if (cursor.getNext().getOpcode() == Opcodes.GOTO) {
// end of comparisons for same hashCode
// jump to default
nextIs(Opcodes.GOTO);
+ replacements.add(defaultLabel, cursor, 0);
break;
} else if (cursor.getNext() == defaultLabel) {
+ replacements.add(defaultLabel, cursor, 0);
break;
}
}
}
if (ifNullInstruction != null) {
- replacements.add(skipNonOpcodes(ifNullInstruction.label));
+ replacements.add(ifNullInstruction.label, ifNullInstruction, 1);
}
output.ignore(start.getNext(), cursor);
diff --git a/org.jacoco.doc/docroot/doc/changes.html b/org.jacoco.doc/docroot/doc/changes.html
index 42d8256d37..0d2a40cfee 100644
--- a/org.jacoco.doc/docroot/doc/changes.html
+++ b/org.jacoco.doc/docroot/doc/changes.html
@@ -27,6 +27,16 @@
+ * data class B(val c: String)
+ * fun example(b: B?): String =
+ * b?.c ?: ""
+ *
+ */
+ @Test
+ public void should_filter_safe_call_followed_by_elvis() {
+ final MethodNode m = new MethodNode(InstrSupport.ASM_API_VERSION, 0,
+ "example", "(LB;)Ljava/lang/String;", null, null);
+ m.visitVarInsn(Opcodes.ALOAD, 0);
+ final Label label1 = new Label();
+ final Label label2 = new Label();
+
+ m.visitInsn(Opcodes.DUP);
+ m.visitJumpInsn(Opcodes.IFNULL, label1);
+ final AbstractInsnNode ifNullInstruction = m.instructions.getLast();
+ m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "B", "getC",
+ "()Ljava/lang/String;", false);
+
+ m.visitInsn(Opcodes.DUP);
+ m.visitJumpInsn(Opcodes.IFNONNULL, label2);
+ final AbstractInsnNode ifNonNullInstruction = m.instructions.getLast();
+ m.visitLabel(label1);
+ m.visitInsn(Opcodes.POP);
+ final AbstractInsnNode popInstruction = m.instructions.getLast();
+ m.visitLdcInsn("");
+ m.visitLabel(label2);
+
+ filter.filter(m, context, output);
+
+ assertIgnored(m);
+ final HashMap> replacements = new HashMap>();
+ replacements.put(ifNullInstruction, Arrays.asList( //
+ new Replacement(0, ifNullInstruction, 0),
+ new Replacement(1, popInstruction, 0)));
+ replacements.put(ifNonNullInstruction, Arrays.asList( //
+ new Replacement(0, popInstruction, 0),
+ new Replacement(1, ifNonNullInstruction, 1)));
+ assertReplacedBranches(m, replacements);
+ }
+
+ /**
+ *
+ * data class A(val b: B)
+ * data class B(val c: String)
+ * fun example(a: A?): String =
+ * a?.b?.c ?: ""
+ *
+ */
+ @Test
+ public void should_filter_safe_call_chain_followed_by_elvis() {
+ final MethodNode m = new MethodNode(InstrSupport.ASM_API_VERSION, 0,
+ "example", "(LA;)Ljava/lang/String;", null, null);
+ m.visitVarInsn(Opcodes.ALOAD, 1);
+ final Label label1 = new Label();
+ final Label label2 = new Label();
+
+ m.visitInsn(Opcodes.DUP);
+ m.visitJumpInsn(Opcodes.IFNULL, label1);
+ final AbstractInsnNode ifNullInstruction1 = m.instructions.getLast();
+ m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "A", "getB", "()LB;", false);
+
+ m.visitInsn(Opcodes.DUP);
+ m.visitJumpInsn(Opcodes.IFNULL, label1);
+ final AbstractInsnNode ifNullInstruction2 = m.instructions.getLast();
+ m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "B", "getC",
+ "()Ljava/lang/String;", false);
+
+ m.visitInsn(Opcodes.DUP);
+ m.visitJumpInsn(Opcodes.IFNONNULL, label2);
+ final AbstractInsnNode ifNonNullInstruction = m.instructions.getLast();
+ m.visitLabel(label1);
+ m.visitInsn(Opcodes.POP);
+ final AbstractInsnNode popInstruction = m.instructions.getLast();
+ m.visitLdcInsn("");
+ m.visitLabel(label2);
+
+ filter.filter(m, context, output);
+
+ assertIgnored(m);
+ final HashMap> replacements = new HashMap>();
+ replacements.put(ifNullInstruction1, Arrays.asList( //
+ new Replacement(0, ifNullInstruction1, 0),
+ new Replacement(1, popInstruction, 0)));
+ replacements.put(ifNullInstruction2, Arrays.asList( //
+ new Replacement(0, ifNullInstruction2, 0),
+ new Replacement(1, popInstruction, 0)));
+ replacements.put(ifNonNullInstruction, Arrays.asList( //
+ new Replacement(0, popInstruction, 0),
+ new Replacement(1, ifNonNullInstruction, 1)));
+ assertReplacedBranches(m, replacements);
+ }
+
}
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinSafeCallOperatorFilter.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinSafeCallOperatorFilter.java
index 9b272e765c..8fce63c698 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinSafeCallOperatorFilter.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinSafeCallOperatorFilter.java
@@ -32,19 +32,26 @@ final class KotlinSafeCallOperatorFilter implements IFilter {
public void filter(final MethodNode methodNode,
final IFilterContext context, final IFilterOutput output) {
for (final ArrayList chain : findChains(methodNode)) {
- if (chain.size() == 1) {
+ final AbstractInsnNode ifNonNullInstruction = chain.get(0).label
+ .getPrevious();
+ if (chain.size() == 1
+ && ifNonNullInstruction.getOpcode() != Opcodes.IFNONNULL) {
continue;
}
- final JumpInsnNode lastIfNullInstruction = chain
- .get(chain.size() - 1);
final AbstractInsnNode nullTarget = AbstractMatcher
- .skipNonOpcodes(lastIfNullInstruction.label);
+ .skipNonOpcodes(chain.get(0).label);
for (final AbstractInsnNode ifNullInstruction : chain) {
final Replacements replacements = new Replacements();
replacements.add(ifNullInstruction, ifNullInstruction, 0);
replacements.add(nullTarget, nullTarget, 0);
output.replaceBranches(ifNullInstruction, replacements);
}
+ if (ifNonNullInstruction.getOpcode() == Opcodes.IFNONNULL) {
+ final Replacements replacements = new Replacements();
+ replacements.add(nullTarget, nullTarget, 0);
+ replacements.add(ifNonNullInstruction, ifNonNullInstruction, 1);
+ output.replaceBranches(ifNonNullInstruction, replacements);
+ }
}
}
diff --git a/org.jacoco.doc/docroot/doc/changes.html b/org.jacoco.doc/docroot/doc/changes.html
index a1e696a8f4..a1fd430b3c 100644
--- a/org.jacoco.doc/docroot/doc/changes.html
+++ b/org.jacoco.doc/docroot/doc/changes.html
@@ -25,6 +25,9 @@
New Features
Branches added by the Kotlin compiler for default argument number 33 or higher
are filtered out during generation of report
(GitHub #1655).
+
Part of bytecode generated by the Kotlin compiler for elvis operator that
+ follows safe call operator is filtered out during generation of report
+ (GitHub #1814).
Does JaCoCo have a plug-in for [Eclipse|Netbeans|Whatever...]?
What Java versions are supported by JaCoCo?
JaCoCo officially supports Java class files from version 1.0 to 24. Also
- experimental support for class files of version 25 is provided.
+ experimental support for class files of version 25 and 26 is provided.
However the minimum JRE version required by the JaCoCo runtime
(e.g. the agent) and the JaCoCo tools is 1.5. Also note that class files under
test from version 1.6 and above have to contain valid stackmap frames.
From e6861dd52e2e1c9723654fdb99bc067d2a89768c Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 9 Jul 2025 10:11:35 +0200
Subject: [PATCH 221/255] Upgrade maven-invoker-plugin to 3.9.1 (#1907)
---
org.jacoco.build/pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/org.jacoco.build/pom.xml b/org.jacoco.build/pom.xml
index 679c8ae850..ac605cd5ff 100644
--- a/org.jacoco.build/pom.xml
+++ b/org.jacoco.build/pom.xml
@@ -338,7 +338,7 @@
org.apache.maven.pluginsmaven-invoker-plugin
- 3.9.0
+ 3.9.1org.apache.maven.plugins
From 2b5ab9a2ca11ad3327cac60657254370a8351c29 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 9 Jul 2025 15:00:28 +0200
Subject: [PATCH 222/255] Upgrade maven-gpg-plugin to 3.2.8 (#1910)
---
org.jacoco.build/pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/org.jacoco.build/pom.xml b/org.jacoco.build/pom.xml
index ac605cd5ff..e60fb39732 100644
--- a/org.jacoco.build/pom.xml
+++ b/org.jacoco.build/pom.xml
@@ -343,7 +343,7 @@
org.apache.maven.pluginsmaven-gpg-plugin
- 3.2.7
+ 3.2.8org.apache.maven.plugins
From 4f6a51b7814730f7bbf5c27c359dc19505ab2758 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 9 Jul 2025 16:04:49 +0200
Subject: [PATCH 223/255] Upgrade maven-enforcer-plugin to 3.6.0 (#1909)
---
org.jacoco.build/pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/org.jacoco.build/pom.xml b/org.jacoco.build/pom.xml
index e60fb39732..65a8449ecd 100644
--- a/org.jacoco.build/pom.xml
+++ b/org.jacoco.build/pom.xml
@@ -328,7 +328,7 @@
org.apache.maven.pluginsmaven-enforcer-plugin
- 3.5.0
+ 3.6.0org.apache.maven.plugins
From a4cee188c95d56f19725144720c1f79004d80527 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 15 Jul 2025 11:43:18 +0200
Subject: [PATCH 224/255] Upgrade spotless-maven-plugin to 2.45.0 (#1917)
---
org.jacoco.build/pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/org.jacoco.build/pom.xml b/org.jacoco.build/pom.xml
index 65a8449ecd..62052b26db 100644
--- a/org.jacoco.build/pom.xml
+++ b/org.jacoco.build/pom.xml
@@ -450,7 +450,7 @@
com.diffplug.spotlessspotless-maven-plugin
- 2.44.5
+ 2.45.0org.sonarsource.scanner.maven
From 97e7f62046c4cdb6c1b365b087974cd8688166dd Mon Sep 17 00:00:00 2001
From: Evgeny Mandrikov <138671+Godin@users.noreply.github.com>
Date: Tue, 29 Jul 2025 15:05:09 +0300
Subject: [PATCH 225/255] Upgrade Maven to 3.9.11 (#1912)
---
.mvn/wrapper/maven-wrapper.properties | 4 ++--
org.jacoco.build/pom.xml | 2 +-
org.jacoco.doc/docroot/doc/environment.html | 2 +-
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties
index fe9deed0b0..546a64e725 100644
--- a/.mvn/wrapper/maven-wrapper.properties
+++ b/.mvn/wrapper/maven-wrapper.properties
@@ -16,5 +16,5 @@
# under the License.
wrapperVersion=3.3.2
distributionType=only-script
-distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip
-distributionSha256Sum=4ec3f26fb1a692473aea0235c300bd20f0f9fe741947c82c1234cefd76ac3a3c
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.11/apache-maven-3.9.11-bin.zip
+distributionSha256Sum=0d7125e8c91097b36edb990ea5934e6c68b4440eef4ea96510a0f6815e7eeadb
diff --git a/org.jacoco.build/pom.xml b/org.jacoco.build/pom.xml
index 62052b26db..a0d561f0b6 100644
--- a/org.jacoco.build/pom.xml
+++ b/org.jacoco.build/pom.xml
@@ -520,7 +520,7 @@
17
- 3.9.9
+ 3.9.11The rules for repo1.maven.org are that pom.xml files should not include repository definitions.
diff --git a/org.jacoco.doc/docroot/doc/environment.html b/org.jacoco.doc/docroot/doc/environment.html
index f56639a9bc..838c209b9a 100644
--- a/org.jacoco.doc/docroot/doc/environment.html
+++ b/org.jacoco.doc/docroot/doc/environment.html
@@ -76,7 +76,7 @@
Build
The JaCoCo build is based on Maven
- and requires at least Maven 3.9.9 and JDK 17.
+ and requires at least Maven 3.9.11 and JDK 17.
See the build description for details.
From 5fda1315076acc29a22589921002f7890930f732 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 29 Jul 2025 15:23:58 +0200
Subject: [PATCH 226/255] Upgrade maven-enforcer-plugin to 3.6.1 (#1927)
---
org.jacoco.build/pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/org.jacoco.build/pom.xml b/org.jacoco.build/pom.xml
index a0d561f0b6..4a452c6420 100644
--- a/org.jacoco.build/pom.xml
+++ b/org.jacoco.build/pom.xml
@@ -328,7 +328,7 @@
org.apache.maven.pluginsmaven-enforcer-plugin
- 3.6.0
+ 3.6.1org.apache.maven.plugins
From ec92eda4c3b84a7cd21a02b1d78691111ea4c294 Mon Sep 17 00:00:00 2001
From: Evgeny Mandrikov <138671+Godin@users.noreply.github.com>
Date: Thu, 7 Aug 2025 01:22:25 +0200
Subject: [PATCH 227/255] KotlinCoroutineFilter should filter inlined
invocations of `suspendCoroutineUninterceptedOrReturn` (#1929)
---
.../kotlin/KotlinCoroutineCallbackTest.java | 27 ++++++
.../targets/KotlinCoroutineCallbackTarget.kt | 49 +++++++++++
.../filter/KotlinCoroutineFilterTest.java | 83 +++++++++++++++++++
.../filter/KotlinCoroutineFilter.java | 35 ++++++++
org.jacoco.doc/docroot/doc/changes.html | 4 +
5 files changed, 198 insertions(+)
create mode 100644 org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinCoroutineCallbackTest.java
create mode 100644 org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinCoroutineCallbackTarget.kt
diff --git a/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinCoroutineCallbackTest.java b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinCoroutineCallbackTest.java
new file mode 100644
index 0000000000..d779f14b44
--- /dev/null
+++ b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinCoroutineCallbackTest.java
@@ -0,0 +1,27 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2025 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.KotlinCoroutineCallbackTarget;
+
+/**
+ * Test of code coverage in {@link KotlinCoroutineCallbackTarget}.
+ */
+public class KotlinCoroutineCallbackTest extends ValidationTestBase {
+
+ public KotlinCoroutineCallbackTest() {
+ super(KotlinCoroutineCallbackTarget.class);
+ }
+
+}
diff --git a/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinCoroutineCallbackTarget.kt b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinCoroutineCallbackTarget.kt
new file mode 100644
index 0000000000..5ec8d243b7
--- /dev/null
+++ b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinCoroutineCallbackTarget.kt
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2025 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 kotlinx.coroutines.runBlocking
+import org.jacoco.core.test.validation.targets.Stubs.nop
+import kotlin.coroutines.resume
+import kotlin.coroutines.suspendCoroutine
+
+/**
+ * Test target containing invocations of [kotlin.coroutines.intrinsics.suspendCoroutineUninterceptedOrReturn] intrinsic
+ * inlined from [suspendCoroutine] which provides a way to
+ * [wrap any callback into a Kotlin suspending function](https://github.com/Kotlin/KEEP/blob/main/proposals/KEEP-0164-coroutines.md#wrapping-callbacks).
+ *
+ * @see [kotlinx.coroutines.suspendCancellableCoroutine]
+ */
+object KotlinCoroutineCallbackTarget {
+
+ suspend fun example() =
+ suspendCoroutine { continuation -> // assertFullyCovered()
+ continuation.resume(Unit) // assertFullyCovered()
+ } // assertFullyCovered()
+
+ suspend fun withoutTailCallOptimization() {
+ suspendCoroutine { continuation -> // assertFullyCovered()
+ continuation.resume(Unit) // assertFullyCovered()
+ } // assertFullyCovered()
+ nop("tail") // assertFullyCovered()
+ }
+
+ @JvmStatic
+ fun main(args: Array) {
+ runBlocking {
+ example()
+ withoutTailCallOptimization()
+ }
+ }
+
+}
diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinCoroutineFilterTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinCoroutineFilterTest.java
index ab906df8f4..2054181367 100644
--- a/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinCoroutineFilterTest.java
+++ b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinCoroutineFilterTest.java
@@ -511,4 +511,87 @@ public void should_filter_Kotlin_1_6_suspending_lambda_without_suspension_points
assertIgnored(m, range0, range1);
}
+ /**
+ *
+ *
+ * @see #should_filter_suspendCoroutineUninterceptedOrReturn()
+ */
+ @Test
+ public void should_filter_suspendCoroutineUninterceptedOrReturn_when_no_tail_call_optimization() {
+ final MethodNode m = new MethodNode(InstrSupport.ASM_API_VERSION,
+ Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL, "example",
+ "(Lkotlin/coroutines/Continuation;)Ljava/lang/Object;", null,
+ null);
+
+ m.visitInsn(Opcodes.NOP);
+
+ m.visitInsn(Opcodes.DUP);
+ final Range range0 = new Range();
+ m.visitMethodInsn(Opcodes.INVOKESTATIC,
+ "kotlin/coroutines/intrinsics/IntrinsicsKt",
+ "getCOROUTINE_SUSPENDED", "()Ljava/lang/Object;", false);
+ final Label label = new Label();
+ m.visitJumpInsn(Opcodes.IF_ACMPNE, label);
+ range0.fromInclusive = m.instructions.getLast();
+ m.visitInsn(Opcodes.ALOAD);
+ m.visitTypeInsn(Opcodes.CHECKCAST, "kotlin/coroutines/Continuation");
+ m.visitMethodInsn(Opcodes.INVOKESTATIC,
+ "kotlin/coroutines/jvm/internal/DebugProbesKt",
+ "probeCoroutineSuspended",
+ "(Lkotlin/coroutines/Continuation;)V", false);
+ range0.toInclusive = m.instructions.getLast();
+ m.visitLabel(label);
+
+ filter.filter(m, context, output);
+
+ assertIgnored(m, range0);
+ }
+
}
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinCoroutineFilter.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinCoroutineFilter.java
index 307710f1a6..40a9b8c829 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinCoroutineFilter.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinCoroutineFilter.java
@@ -32,9 +32,44 @@ public void filter(final MethodNode methodNode,
final IFilterContext context, final IFilterOutput output) {
new Matcher().match(methodNode, output);
new Matcher().matchOptimizedTailCall(methodNode, output);
+ new Matcher().matchSuspendCoroutineUninterceptedOrReturn(methodNode,
+ output);
}
private static class Matcher extends AbstractMatcher {
+ /**
+ * Filters
+ * bytecode generated for inlined invocations of
+ * kotlin.coroutines.intrinsics/suspendCoroutineUninterceptedOrReturn
+ */
+ private void matchSuspendCoroutineUninterceptedOrReturn(
+ final MethodNode methodNode, final IFilterOutput output) {
+ for (final AbstractInsnNode i : methodNode.instructions) {
+ cursor = i;
+ nextIs(Opcodes.DUP);
+ nextIsInvoke(Opcodes.INVOKESTATIC,
+ "kotlin/coroutines/intrinsics/IntrinsicsKt",
+ "getCOROUTINE_SUSPENDED", "()Ljava/lang/Object;");
+ nextIs(Opcodes.IF_ACMPNE);
+ final JumpInsnNode jumpInstruction = (JumpInsnNode) cursor;
+ nextIs(Opcodes.ALOAD);
+ if (cursor != null
+ && cursor.getNext().getOpcode() == Opcodes.CHECKCAST) {
+ nextIsType(Opcodes.CHECKCAST,
+ "kotlin/coroutines/Continuation");
+ }
+ nextIsInvoke(Opcodes.INVOKESTATIC,
+ "kotlin/coroutines/jvm/internal/DebugProbesKt",
+ "probeCoroutineSuspended",
+ "(Lkotlin/coroutines/Continuation;)V");
+ if (cursor != null
+ && jumpInstruction.label == cursor.getNext()) {
+ output.ignore(jumpInstruction, cursor);
+ }
+ }
+ }
private void matchOptimizedTailCall(final MethodNode methodNode,
final IFilterOutput output) {
diff --git a/org.jacoco.doc/docroot/doc/changes.html b/org.jacoco.doc/docroot/doc/changes.html
index 56db2bb3f4..03b5876a82 100644
--- a/org.jacoco.doc/docroot/doc/changes.html
+++ b/org.jacoco.doc/docroot/doc/changes.html
@@ -30,6 +30,10 @@
New Features
Part of bytecode generated by the Kotlin compiler for elvis operator that
follows safe call operator is filtered out during generation of report
(GitHub #1814).
+
Part of bytecode generated by the Kotlin compiler for invocations of
+ suspendCoroutineUninterceptedOrReturn intrinsic is filtered out
+ during generation of report
+ (GitHub #1929).
for {@link org.objectweb.asm.tree.JumpInsnNode} with
- * {@link org.objectweb.asm.Opcodes#GOTO} there is only branch 0 that
+ * {@link org.objectweb.asm.Opcodes#GOTO} there is only branch 1 that
* corresponds to continuation of execution at
* {@link org.objectweb.asm.tree.JumpInsnNode#label}
*
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/StringSwitchFilter.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/StringSwitchFilter.java
index 17d62a802c..3d8b041c7b 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/StringSwitchFilter.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/StringSwitchFilter.java
@@ -107,7 +107,7 @@ public void match(final AbstractInsnNode start,
// end of comparisons for same hashCode
// jump to default
nextIs(Opcodes.GOTO);
- replacements.add(defaultLabel, cursor, 0);
+ replacements.add(defaultLabel, cursor, 1);
break;
} else if (cursor.getNext() == defaultLabel) {
replacements.add(defaultLabel, cursor, 0);
diff --git a/org.jacoco.doc/docroot/doc/changes.html b/org.jacoco.doc/docroot/doc/changes.html
index 03b5876a82..d3855a7255 100644
--- a/org.jacoco.doc/docroot/doc/changes.html
+++ b/org.jacoco.doc/docroot/doc/changes.html
@@ -40,10 +40,12 @@
Fixed bugs
Fixed handling of implicit else clause of when with
String subject in Kotlin
- (GitHub #1813).
+ *
+ * This became
+ * the default in Compose Kotlin compiler plugin version 2.2.0
+ */
+ @Test
+ public void should_filter_pausable_composition() {
+ final MethodNode m = new MethodNode(InstrSupport.ASM_API_VERSION, 0,
+ "example", "(Landroidx/compose/runtime/Composer;I)V", null,
+ null);
+ m.visitAnnotation("Landroidx/compose/runtime/Composable;", false);
+
+ final Range range1 = new Range();
+ final Range range2 = new Range();
+
+ m.visitVarInsn(ALOAD, 0);
+ range1.fromInclusive = m.instructions.getLast();
+ m.visitLdcInsn(Integer.valueOf(-933543558));
+ m.visitMethodInsn(INVOKEINTERFACE, "androidx/compose/runtime/Composer",
+ "startRestartGroup", "(I)Landroidx/compose/runtime/Composer;",
+ true);
+ m.visitVarInsn(ASTORE, 0);
+ m.visitVarInsn(ALOAD, 0);
+ m.visitVarInsn(ILOAD, 1);
+ final Label label1 = new Label();
+ m.visitJumpInsn(IFEQ, label1);
+ m.visitInsn(ICONST_1);
+ final Label label2 = new Label();
+ m.visitJumpInsn(GOTO, label2);
+ m.visitLabel(label1);
+ m.visitInsn(ICONST_0);
+ m.visitLabel(label2);
+ m.visitVarInsn(ILOAD, 1);
+ m.visitInsn(ICONST_1);
+ m.visitInsn(IAND);
+ m.visitMethodInsn(INVOKEINTERFACE, "androidx/compose/runtime/Composer",
+ "shouldExecute", "(ZI)Z", true);
+ final Label label3 = new Label();
+ m.visitJumpInsn(IFEQ, label3);
+ range1.toInclusive = m.instructions.getLast();
+
+ // body
+ final Label label5 = new Label();
+ m.visitJumpInsn(GOTO, label5);
+
+ m.visitLabel(label3);
+ m.visitVarInsn(ALOAD, 0);
+ m.visitMethodInsn(INVOKEINTERFACE, "androidx/compose/runtime/Composer",
+ "skipToGroupEnd", "()V", true);
+ m.visitLabel(label5);
+ m.visitVarInsn(ALOAD, 0);
+ m.visitMethodInsn(INVOKEINTERFACE, "androidx/compose/runtime/Composer",
+ "endRestartGroup",
+ "()Landroidx/compose/runtime/ScopeUpdateScope;", true);
+ m.visitInsn(DUP);
+ final Label label6 = new Label();
+ m.visitJumpInsn(IFNULL, label6);
+ range2.fromInclusive = m.instructions.getLast();
+ m.visitVarInsn(ILOAD, 1);
+ m.visitInvokeDynamicInsn("invoke",
+ "(I)Lkotlin/jvm/functions/Function2;", null);
+ m.visitMethodInsn(INVOKEINTERFACE,
+ "androidx/compose/runtime/ScopeUpdateScope", "updateScope",
+ "(Lkotlin/jvm/functions/Function2;)V", true);
+ final Label label7 = new Label();
+ m.visitJumpInsn(GOTO, label7);
+ m.visitLabel(label6);
+ m.visitInsn(POP);
+ range2.toInclusive = m.instructions.getLast();
+
+ m.visitLabel(label7);
+ m.visitInsn(RETURN);
+
+ filter.filter(m, context, output);
+
+ assertIgnored(m, range1, range2);
+ }
+
}
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinComposeFilter.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinComposeFilter.java
index f3c01d96df..bbb25b2cb5 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinComposeFilter.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinComposeFilter.java
@@ -35,6 +35,13 @@ public void filter(final MethodNode methodNode,
}
final MethodInsnNode mi = (MethodInsnNode) i;
if ("androidx/compose/runtime/Composer".equals(mi.owner)
+ && "shouldExecute".equals(mi.name)
+ && "(ZI)Z".equals(mi.desc)
+ && mi.getNext().getOpcode() == Opcodes.IFEQ) {
+ // https://github.com/JetBrains/kotlin/commit/ee9217f8f0f37967684fbfe4a568c2b3c8707507
+ final JumpInsnNode ji = (JumpInsnNode) mi.getNext();
+ output.ignore(methodNode.instructions.getFirst(), ji);
+ } else if ("androidx/compose/runtime/Composer".equals(mi.owner)
&& "getSkipping".equals(mi.name) && "()Z".equals(mi.desc)
&& mi.getNext().getOpcode() == Opcodes.IFNE) {
// https://github.com/JetBrains/kotlin/blob/v2.0.0-RC2/plugins/compose/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableFunctionBodyTransformer.kt#L361-L384
diff --git a/org.jacoco.doc/docroot/doc/changes.html b/org.jacoco.doc/docroot/doc/changes.html
index d3855a7255..13941a8c61 100644
--- a/org.jacoco.doc/docroot/doc/changes.html
+++ b/org.jacoco.doc/docroot/doc/changes.html
@@ -34,6 +34,9 @@
New Features
suspendCoroutineUninterceptedOrReturn intrinsic is filtered out
during generation of report
(GitHub #1929).
+
Part of bytecode generated by the Kotlin Compose compiler plugin for pausable
+ composition is filtered out during generation of report
+ (GitHub #1911).
Fixed bugs
From deae0ad1f541990ea077023abdcacdc06a7e9184 Mon Sep 17 00:00:00 2001
From: Sander Jochems
Date: Wed, 10 Sep 2025 00:15:28 +0200
Subject: [PATCH 236/255] Bump org.apache.maven.shared:file-management from
3.1.0 to 3.2.0 (#1947)
---
jacoco-maven-plugin/pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/jacoco-maven-plugin/pom.xml b/jacoco-maven-plugin/pom.xml
index 61940a50a3..9345168453 100644
--- a/jacoco-maven-plugin/pom.xml
+++ b/jacoco-maven-plugin/pom.xml
@@ -59,7 +59,7 @@
org.apache.maven.sharedfile-management
- 3.1.0
+ 3.2.0
From 0d2405a78df6a07ee8da2bae6b8a910d2840c934 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sun, 14 Sep 2025 22:44:57 +0000
Subject: [PATCH 237/255] Upgrade Kotlin to 2.2.20 (#1948)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Evgeny Mandrikov
---
org.jacoco.core.test.validation.kotlin/pom.xml | 2 +-
.../test/validation/kotlin/targets/KotlinJvmOverloadsTarget.kt | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/org.jacoco.core.test.validation.kotlin/pom.xml b/org.jacoco.core.test.validation.kotlin/pom.xml
index 5311e1496e..6195ad4005 100644
--- a/org.jacoco.core.test.validation.kotlin/pom.xml
+++ b/org.jacoco.core.test.validation.kotlin/pom.xml
@@ -25,7 +25,7 @@
JaCoCo :: Test :: Core :: Validation Kotlin
- 2.2.10
+ 2.2.20
diff --git a/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinJvmOverloadsTarget.kt b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinJvmOverloadsTarget.kt
index 408be57452..749ad7099a 100644
--- a/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinJvmOverloadsTarget.kt
+++ b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinJvmOverloadsTarget.kt
@@ -19,7 +19,7 @@ import org.jacoco.core.test.validation.targets.Stubs.nop
*/
object KotlinJvmOverloadsTarget {
- @JvmOverloads // assertFullyCovered()
+ @JvmOverloads // assertEmpty()
fun example(p1: String = "p1", p2: String = "p2") { // assertFullyCovered()
nop(p1 + p2) // assertFullyCovered()
} // assertFullyCovered()
From b7b8966dc2018ed00a7e278ff0bcc02d3613762c Mon Sep 17 00:00:00 2001
From: Evgeny Mandrikov <138671+Godin@users.noreply.github.com>
Date: Wed, 17 Sep 2025 01:19:50 +0200
Subject: [PATCH 238/255] KotlinCoroutineFilter should filter Kotlin 2.2
suspending lambdas with parameters (#1945)
---
.../KotlinCoroutineSuspendingLambdaTest.java | 27 +++++
.../KotlinCoroutineSuspendingLambdaTarget.kt | 43 ++++++++
.../filter/KotlinCoroutineFilterTest.java | 99 +++++++++++++++++++
.../filter/KotlinCoroutineFilter.java | 80 +++------------
org.jacoco.doc/docroot/doc/changes.html | 3 +
5 files changed, 183 insertions(+), 69 deletions(-)
create mode 100644 org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinCoroutineSuspendingLambdaTest.java
create mode 100644 org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinCoroutineSuspendingLambdaTarget.kt
diff --git a/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinCoroutineSuspendingLambdaTest.java b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinCoroutineSuspendingLambdaTest.java
new file mode 100644
index 0000000000..3eebad5556
--- /dev/null
+++ b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinCoroutineSuspendingLambdaTest.java
@@ -0,0 +1,27 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2025 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.KotlinCoroutineSuspendingLambdaTarget;
+
+/**
+ * Test of code coverage in {@link KotlinCoroutineSuspendingLambdaTarget}.
+ */
+public class KotlinCoroutineSuspendingLambdaTest extends ValidationTestBase {
+
+ public KotlinCoroutineSuspendingLambdaTest() {
+ super(KotlinCoroutineSuspendingLambdaTarget.class);
+ }
+
+}
diff --git a/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinCoroutineSuspendingLambdaTarget.kt b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinCoroutineSuspendingLambdaTarget.kt
new file mode 100644
index 0000000000..a803f7f43b
--- /dev/null
+++ b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinCoroutineSuspendingLambdaTarget.kt
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2025 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 kotlinx.coroutines.runBlocking
+
+/**
+ * Test target containing suspending lambdas.
+ */
+object KotlinCoroutineSuspendingLambdaTarget {
+
+ private fun withParameter() {
+ fun exec(block: suspend (p: String) -> Unit): Unit = runBlocking { block("") }
+ fun noexec(block: suspend (p: String) -> Unit) = Unit
+ suspend fun suspensionPoint(p: String) = Unit
+
+ exec { p -> // assertFullyCovered()
+ suspensionPoint(p) // assertFullyCovered()
+ } // assertFullyCovered()
+
+ noexec { p -> // assertFullyCovered()
+ suspensionPoint(p) // assertNotCovered()
+ } // assertNotCovered()
+
+ noexec { p -> suspensionPoint(p) } // assertPartlyCovered()
+ }
+
+ @JvmStatic
+ fun main(args: Array) {
+ withParameter()
+ }
+
+}
diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinCoroutineFilterTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinCoroutineFilterTest.java
index 2054181367..7688d91348 100644
--- a/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinCoroutineFilterTest.java
+++ b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinCoroutineFilterTest.java
@@ -221,6 +221,105 @@ public void should_filter_suspending_lambdas() {
assertIgnored(m, range0, range1, range2);
}
+ /**
+ *
+ * fun exec(block: suspend (p: String) -> Unit): Unit
+ *
+ * fun main() =
+ * exec { p ->
+ * suspensionPoint(p)
+ * }
+ *
suspendCoroutineUninterceptedOrReturn intrinsic is filtered out
during generation of report
(GitHub #1929).
+
Part of bytecode generated by the Kotlin compiler for suspending lambdas with
+ parameters is filtered out during generation of report
+ (GitHub #1945).
Part of bytecode generated by the Kotlin Compose compiler plugin for pausable
composition is filtered out during generation of report
(GitHub #1911).
JaCoCo now officially supports Java 25
+ (GitHub #1950).
Experimental support for Java 26 class files
(GitHub #1870).
Branches added by the Kotlin compiler for default argument number 33 or higher
diff --git a/org.jacoco.doc/docroot/doc/environment.html b/org.jacoco.doc/docroot/doc/environment.html
index 838c209b9a..69d72f3610 100644
--- a/org.jacoco.doc/docroot/doc/environment.html
+++ b/org.jacoco.doc/docroot/doc/environment.html
@@ -69,7 +69,7 @@
JRE/JDK
The minimum supported JRE version for JaCoCo is Java 5. To guarantee
compatibility JaCoCo release builds should always be executed using JDK 5.
In addition we run builds with 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17
- 18, 19, 20, 21, 22, 23, 24 and 25 JDKs.
+ 18, 19, 20, 21, 22, 23, 24, 25 and 26 JDKs.
Does JaCoCo have a plug-in for [Eclipse|Netbeans|Whatever...]?
What Java versions are supported by JaCoCo?
- JaCoCo officially supports Java class files from version 1.0 to 24. Also
- experimental support for class files of version 25 and 26 is provided.
+ JaCoCo officially supports Java class files from version 1.0 to 25. Also
+ experimental support for class files of version 26 is provided.
However the minimum JRE version required by the JaCoCo runtime
(e.g. the agent) and the JaCoCo tools is 1.5. Also note that class files under
test from version 1.6 and above have to contain valid stackmap frames.
From fd2b323b7f4aa7a9548b05cd5a444e3e1ff3c86b Mon Sep 17 00:00:00 2001
From: Evgeny Mandrikov <138671+Godin@users.noreply.github.com>
Date: Mon, 22 Sep 2025 23:39:27 +0200
Subject: [PATCH 240/255] KotlinCoroutineFilter should filter suspension points
that return inline value classes (#1871)
---
.../KotlinCoroutineInlineValueClassTest.java | 27 ++++++
.../KotlinCoroutineInlineValueClassTarget.kt | 35 +++++++
.../filter/KotlinCoroutineFilterTest.java | 94 +++++++++++++++++++
.../filter/KotlinCoroutineFilter.java | 25 +----
org.jacoco.doc/docroot/doc/changes.html | 4 +
5 files changed, 165 insertions(+), 20 deletions(-)
create mode 100644 org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinCoroutineInlineValueClassTest.java
create mode 100644 org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinCoroutineInlineValueClassTarget.kt
diff --git a/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinCoroutineInlineValueClassTest.java b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinCoroutineInlineValueClassTest.java
new file mode 100644
index 0000000000..f84c7ea5d4
--- /dev/null
+++ b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinCoroutineInlineValueClassTest.java
@@ -0,0 +1,27 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2025 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.KotlinCoroutineInlineValueClassTarget;
+
+/**
+ * Test of code coverage in {@link KotlinCoroutineInlineValueClassTarget}.
+ */
+public class KotlinCoroutineInlineValueClassTest extends ValidationTestBase {
+
+ public KotlinCoroutineInlineValueClassTest() {
+ super(KotlinCoroutineInlineValueClassTarget.class);
+ }
+
+}
diff --git a/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinCoroutineInlineValueClassTarget.kt b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinCoroutineInlineValueClassTarget.kt
new file mode 100644
index 0000000000..bca7c92d5c
--- /dev/null
+++ b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinCoroutineInlineValueClassTarget.kt
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2025 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 kotlinx.coroutines.runBlocking
+import org.jacoco.core.test.validation.targets.Stubs.nop
+
+/**
+ * Test target with invocation of suspending function that returns inline value class.
+ */
+object KotlinCoroutineInlineValueClassTarget {
+
+ suspend fun suspensionPointReturningInlineValueClass() = InlineValueClass("")
+
+ @JvmInline
+ value class InlineValueClass(val value: String)
+
+ @JvmStatic
+ fun main(args: Array) {
+ runBlocking { // assertFullyCovered()
+ nop(suspensionPointReturningInlineValueClass()) // assertFullyCovered()
+ } // assertFullyCovered()
+ }
+
+}
diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinCoroutineFilterTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinCoroutineFilterTest.java
index 7688d91348..fbbd23db17 100644
--- a/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinCoroutineFilterTest.java
+++ b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinCoroutineFilterTest.java
@@ -610,6 +610,100 @@ public void should_filter_Kotlin_1_6_suspending_lambda_without_suspension_points
assertIgnored(m, range0, range1);
}
+ /**
+ *
Part of bytecode generated by the Kotlin compiler for suspending lambdas with
parameters is filtered out during generation of report
(GitHub #1945).
+
Part of bytecode generated by the Kotlin compiler for suspending functions and
+ lambdas with suspension points that return inline value class is filtered out
+ during generation of report
+ (GitHub #1871).
Part of bytecode generated by the Kotlin Compose compiler plugin for pausable
composition is filtered out during generation of report
(GitHub #1911).
From 5d10738d4a0587371f54a913cd65c2bad716074a Mon Sep 17 00:00:00 2001
From: Evgeny Mandrikov <138671+Godin@users.noreply.github.com>
Date: Sun, 28 Sep 2025 01:51:54 +0200
Subject: [PATCH 241/255] Upgrade Maven Wrapper to 3.3.4 (#1955)
---
.mvn/wrapper/maven-wrapper.properties | 18 +--------
mvnw | 50 ++++++++++++++++++++----
mvnw.cmd | 56 +++++++++++++++++++++++----
3 files changed, 92 insertions(+), 32 deletions(-)
diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties
index 546a64e725..222cba3baf 100644
--- a/.mvn/wrapper/maven-wrapper.properties
+++ b/.mvn/wrapper/maven-wrapper.properties
@@ -1,20 +1,4 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-wrapperVersion=3.3.2
+wrapperVersion=3.3.4
distributionType=only-script
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.11/apache-maven-3.9.11-bin.zip
distributionSha256Sum=0d7125e8c91097b36edb990ea5934e6c68b4440eef4ea96510a0f6815e7eeadb
diff --git a/mvnw b/mvnw
index 19529ddf8c..bd8896bf22 100755
--- a/mvnw
+++ b/mvnw
@@ -19,7 +19,7 @@
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
-# Apache Maven Wrapper startup batch script, version 3.3.2
+# Apache Maven Wrapper startup batch script, version 3.3.4
#
# Optional ENV vars
# -----------------
@@ -105,14 +105,17 @@ trim() {
printf "%s" "${1}" | tr -d '[:space:]'
}
+scriptDir="$(dirname "$0")"
+scriptName="$(basename "$0")"
+
# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties
while IFS="=" read -r key value; do
case "${key-}" in
distributionUrl) distributionUrl=$(trim "${value-}") ;;
distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;;
esac
-done <"${0%/*}/.mvn/wrapper/maven-wrapper.properties"
-[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties"
+done <"$scriptDir/.mvn/wrapper/maven-wrapper.properties"
+[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
case "${distributionUrl##*/}" in
maven-mvnd-*bin.*)
@@ -130,7 +133,7 @@ maven-mvnd-*bin.*)
distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip"
;;
maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;;
-*) MVN_CMD="mvn${0##*/mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;;
+*) MVN_CMD="mvn${scriptName#mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;;
esac
# apply MVNW_REPOURL and calculate MAVEN_HOME
@@ -227,7 +230,7 @@ if [ -n "${distributionSha256Sum-}" ]; then
echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
exit 1
elif command -v sha256sum >/dev/null; then
- if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c >/dev/null 2>&1; then
+ if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c - >/dev/null 2>&1; then
distributionSha256Result=true
fi
elif command -v shasum >/dev/null; then
@@ -252,8 +255,41 @@ if command -v unzip >/dev/null; then
else
tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar"
fi
-printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url"
-mv -- "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME"
+
+# Find the actual extracted directory name (handles snapshots where filename != directory name)
+actualDistributionDir=""
+
+# First try the expected directory name (for regular distributions)
+if [ -d "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" ]; then
+ if [ -f "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/bin/$MVN_CMD" ]; then
+ actualDistributionDir="$distributionUrlNameMain"
+ fi
+fi
+
+# If not found, search for any directory with the Maven executable (for snapshots)
+if [ -z "$actualDistributionDir" ]; then
+ # enable globbing to iterate over items
+ set +f
+ for dir in "$TMP_DOWNLOAD_DIR"/*; do
+ if [ -d "$dir" ]; then
+ if [ -f "$dir/bin/$MVN_CMD" ]; then
+ actualDistributionDir="$(basename "$dir")"
+ break
+ fi
+ fi
+ done
+ set -f
+fi
+
+if [ -z "$actualDistributionDir" ]; then
+ verbose "Contents of $TMP_DOWNLOAD_DIR:"
+ verbose "$(ls -la "$TMP_DOWNLOAD_DIR")"
+ die "Could not find Maven distribution directory in extracted archive"
+fi
+
+verbose "Found extracted Maven distribution directory: $actualDistributionDir"
+printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$actualDistributionDir/mvnw.url"
+mv -- "$TMP_DOWNLOAD_DIR/$actualDistributionDir" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME"
clean || :
exec_maven "$@"
diff --git a/mvnw.cmd b/mvnw.cmd
index b150b91ed5..5761d94892 100644
--- a/mvnw.cmd
+++ b/mvnw.cmd
@@ -19,7 +19,7 @@
@REM ----------------------------------------------------------------------------
@REM ----------------------------------------------------------------------------
-@REM Apache Maven Wrapper startup batch script, version 3.3.2
+@REM Apache Maven Wrapper startup batch script, version 3.3.4
@REM
@REM Optional ENV vars
@REM MVNW_REPOURL - repo url base for downloading maven distribution
@@ -40,7 +40,7 @@
@SET __MVNW_ARG0_NAME__=
@SET MVNW_USERNAME=
@SET MVNW_PASSWORD=
-@IF NOT "%__MVNW_CMD__%"=="" (%__MVNW_CMD__% %*)
+@IF NOT "%__MVNW_CMD__%"=="" ("%__MVNW_CMD__%" %*)
@echo Cannot start maven from wrapper >&2 && exit /b 1
@GOTO :EOF
: end batch / begin powershell #>
@@ -73,16 +73,30 @@ switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) {
# apply MVNW_REPOURL and calculate MAVEN_HOME
# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/
if ($env:MVNW_REPOURL) {
- $MVNW_REPO_PATTERN = if ($USE_MVND) { "/org/apache/maven/" } else { "/maven/mvnd/" }
- $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')"
+ $MVNW_REPO_PATTERN = if ($USE_MVND -eq $False) { "/org/apache/maven/" } else { "/maven/mvnd/" }
+ $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace "^.*$MVNW_REPO_PATTERN",'')"
}
$distributionUrlName = $distributionUrl -replace '^.*/',''
$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$',''
-$MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain"
+
+$MAVEN_M2_PATH = "$HOME/.m2"
if ($env:MAVEN_USER_HOME) {
- $MAVEN_HOME_PARENT = "$env:MAVEN_USER_HOME/wrapper/dists/$distributionUrlNameMain"
+ $MAVEN_M2_PATH = "$env:MAVEN_USER_HOME"
+}
+
+if (-not (Test-Path -Path $MAVEN_M2_PATH)) {
+ New-Item -Path $MAVEN_M2_PATH -ItemType Directory | Out-Null
+}
+
+$MAVEN_WRAPPER_DISTS = $null
+if ((Get-Item $MAVEN_M2_PATH).Target[0] -eq $null) {
+ $MAVEN_WRAPPER_DISTS = "$MAVEN_M2_PATH/wrapper/dists"
+} else {
+ $MAVEN_WRAPPER_DISTS = (Get-Item $MAVEN_M2_PATH).Target[0] + "/wrapper/dists"
}
-$MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join ''
+
+$MAVEN_HOME_PARENT = "$MAVEN_WRAPPER_DISTS/$distributionUrlNameMain"
+$MAVEN_HOME_NAME = ([System.Security.Cryptography.SHA256]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join ''
$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME"
if (Test-Path -Path "$MAVEN_HOME" -PathType Container) {
@@ -134,7 +148,33 @@ if ($distributionSha256Sum) {
# unzip and move
Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null
-Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null
+
+# Find the actual extracted directory name (handles snapshots where filename != directory name)
+$actualDistributionDir = ""
+
+# First try the expected directory name (for regular distributions)
+$expectedPath = Join-Path "$TMP_DOWNLOAD_DIR" "$distributionUrlNameMain"
+$expectedMvnPath = Join-Path "$expectedPath" "bin/$MVN_CMD"
+if ((Test-Path -Path $expectedPath -PathType Container) -and (Test-Path -Path $expectedMvnPath -PathType Leaf)) {
+ $actualDistributionDir = $distributionUrlNameMain
+}
+
+# If not found, search for any directory with the Maven executable (for snapshots)
+if (!$actualDistributionDir) {
+ Get-ChildItem -Path "$TMP_DOWNLOAD_DIR" -Directory | ForEach-Object {
+ $testPath = Join-Path $_.FullName "bin/$MVN_CMD"
+ if (Test-Path -Path $testPath -PathType Leaf) {
+ $actualDistributionDir = $_.Name
+ }
+ }
+}
+
+if (!$actualDistributionDir) {
+ Write-Error "Could not find Maven distribution directory in extracted archive"
+}
+
+Write-Verbose "Found extracted Maven distribution directory: $actualDistributionDir"
+Rename-Item -Path "$TMP_DOWNLOAD_DIR/$actualDistributionDir" -NewName $MAVEN_HOME_NAME | Out-Null
try {
Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null
} catch {
From 722ab408e6d1329929e221b8d3921db23a528cfb Mon Sep 17 00:00:00 2001
From: Evgeny Mandrikov <138671+Godin@users.noreply.github.com>
Date: Mon, 29 Sep 2025 09:36:12 +0200
Subject: [PATCH 242/255] Upgrade scala-maven-plugin to 4.5.6 (#1957)
---
org.jacoco.core.test.validation.scala/pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/org.jacoco.core.test.validation.scala/pom.xml b/org.jacoco.core.test.validation.scala/pom.xml
index 8667081dd8..d3183cbf36 100644
--- a/org.jacoco.core.test.validation.scala/pom.xml
+++ b/org.jacoco.core.test.validation.scala/pom.xml
@@ -45,7 +45,7 @@
net.alchim31.mavenscala-maven-plugin
- 4.4.0
+ 4.5.6compile
From 8b18051d8babe55d626c6d6d26715b0f0bed6cb7 Mon Sep 17 00:00:00 2001
From: Evgeny Mandrikov <138671+Godin@users.noreply.github.com>
Date: Mon, 29 Sep 2025 16:08:44 +0200
Subject: [PATCH 243/255] Add configuration for Dependabot to simplify updates
of Groovy (#1960)
---
.github/dependabot.yml | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 9271f24667..db72d0172c 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -51,3 +51,16 @@ updates:
- "language: Kotlin"
allow:
- dependency-name: "org.jetbrains.kotlin:*"
+ - package-ecosystem: "maven"
+ directory: "/org.jacoco.core.test.validation.groovy"
+ schedule:
+ interval: "weekly"
+ labels:
+ - "dependencies"
+ - "component: test"
+ - "language: Groovy"
+ allow:
+ - dependency-name: "org.codehaus.groovy:*"
+ ignore:
+ - dependency-name: "org.codehaus.groovy:*"
+ versions: ">=4.0.0"
From daaa3bbbf3378dfe23bd73efe802e9a107d466f9 Mon Sep 17 00:00:00 2001
From: Evgeny Mandrikov <138671+Godin@users.noreply.github.com>
Date: Mon, 29 Sep 2025 18:49:31 +0200
Subject: [PATCH 244/255] Upgrade Groovy to 3.0.25 (#1958)
---
.../pom.xml | 3 ++-
org.jacoco.core.test.validation/pom.xml | 24 +++++++++----------
2 files changed, 13 insertions(+), 14 deletions(-)
diff --git a/org.jacoco.core.test.validation.groovy/pom.xml b/org.jacoco.core.test.validation.groovy/pom.xml
index 019eda0378..b759cdf288 100644
--- a/org.jacoco.core.test.validation.groovy/pom.xml
+++ b/org.jacoco.core.test.validation.groovy/pom.xml
@@ -27,7 +27,8 @@
3.0.0
- 3.0.22
+
+ 3.0.25
diff --git a/org.jacoco.core.test.validation/pom.xml b/org.jacoco.core.test.validation/pom.xml
index 0a7c5f1f83..80c99b848a 100644
--- a/org.jacoco.core.test.validation/pom.xml
+++ b/org.jacoco.core.test.validation/pom.xml
@@ -368,7 +368,7 @@
18
-
+
1618
@@ -395,7 +395,7 @@
19
-
+
1619
@@ -422,7 +422,7 @@
20
-
+
1620
@@ -449,7 +449,7 @@
21
-
+
1621
@@ -477,7 +477,7 @@
22
-
+
1622
@@ -505,7 +505,7 @@
23
-
+
1623
@@ -534,7 +534,7 @@
23
-
+
1624
@@ -547,7 +547,7 @@
../org.jacoco.core.test.validation.java14../org.jacoco.core.test.validation.java16../org.jacoco.core.test.validation.java21
-
../org.jacoco.core.test.validation.scala
@@ -565,7 +565,7 @@
23
-
+
1625
@@ -578,9 +578,7 @@
../org.jacoco.core.test.validation.java14../org.jacoco.core.test.validation.java16../org.jacoco.core.test.validation.java21
-
../org.jacoco.core.test.validation.scala
@@ -596,7 +594,7 @@
23
-
+
1626
@@ -609,7 +607,7 @@
../org.jacoco.core.test.validation.java14../org.jacoco.core.test.validation.java16../org.jacoco.core.test.validation.java21
-
../org.jacoco.core.test.validation.scala
From 7994089f2be32b582c8c8773d82fa5635b2289e5 Mon Sep 17 00:00:00 2001
From: Evgeny Mandrikov <138671+Godin@users.noreply.github.com>
Date: Tue, 30 Sep 2025 10:00:17 +0200
Subject: [PATCH 245/255] Kotlin since version 2.2.0 supports compilation into
Java 24 bytecode (#1959)
---
org.jacoco.core.test.validation.kotlin/pom.xml | 1 +
org.jacoco.core.test.validation/pom.xml | 11 +++++------
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/org.jacoco.core.test.validation.kotlin/pom.xml b/org.jacoco.core.test.validation.kotlin/pom.xml
index 6195ad4005..602d76bad2 100644
--- a/org.jacoco.core.test.validation.kotlin/pom.xml
+++ b/org.jacoco.core.test.validation.kotlin/pom.xml
@@ -25,6 +25,7 @@
JaCoCo :: Test :: Core :: Validation Kotlin
+
2.2.20
diff --git a/org.jacoco.core.test.validation/pom.xml b/org.jacoco.core.test.validation/pom.xml
index 80c99b848a..c4b2d85bca 100644
--- a/org.jacoco.core.test.validation/pom.xml
+++ b/org.jacoco.core.test.validation/pom.xml
@@ -532,8 +532,7 @@
-
- 23
+ 2416
@@ -563,8 +562,8 @@
-
- 23
+
+ 2416
@@ -592,8 +591,8 @@
-
- 23
+
+ 2416
From faa289d2a370fa4f724bed3a09e8591be08cd6c2 Mon Sep 17 00:00:00 2001
From: Evgeny Mandrikov <138671+Godin@users.noreply.github.com>
Date: Tue, 30 Sep 2025 13:25:18 +0200
Subject: [PATCH 246/255] KotlinSafeCallOperatorFilter should not be affected
by presence of pseudo instructions (#1956)
---
.../targets/KotlinSafeCallOperatorTarget.kt | 23 +++++++++++++++++++
.../KotlinSafeCallOperatorFilterTest.java | 5 +++-
.../filter/KotlinSafeCallOperatorFilter.java | 21 ++++++++++++++---
org.jacoco.doc/docroot/doc/changes.html | 3 +++
4 files changed, 48 insertions(+), 4 deletions(-)
diff --git a/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinSafeCallOperatorTarget.kt b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinSafeCallOperatorTarget.kt
index 2f55b3dd5f..ec55a8f902 100644
--- a/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinSafeCallOperatorTarget.kt
+++ b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinSafeCallOperatorTarget.kt
@@ -57,6 +57,28 @@ object KotlinSafeCallOperatorTarget {
}
private fun safeCallChainMultiline() {
+ fun nullOnly(a: A?): String? =
+ a // assertNotCovered()
+ ?.b // assertPartlyCovered(1, 1)
+ ?.c // assertPartlyCovered(1, 1)
+
+ fun nonNullOnly(a: A?): String? =
+ a // assertFullyCovered()
+ ?.b // assertPartlyCovered(1, 1)
+ ?.c // assertFullyCovered(1, 1)
+
+ fun fullCoverage(a: A?): String? =
+ a // assertFullyCovered()
+ ?.b // assertFullyCovered(0, 2)
+ ?.c // assertFullyCovered(0, 2)
+
+ nullOnly(null)
+ nonNullOnly(A(B("")))
+ fullCoverage(null)
+ fullCoverage(A(B("")))
+ }
+
+ private fun safeCallChainMultiline2() {
fun nullOnly(a: A?): String? =
a?.also { // assertPartlyCovered(1, 1)
nop(it) // assertNotCovered()
@@ -125,6 +147,7 @@ object KotlinSafeCallOperatorTarget {
safeCall()
safeCallChain()
safeCallChainMultiline()
+ safeCallChainMultiline2()
safeCallChainException()
safeCallFollowedByElvis()
safeCallChainFollowedByElvis()
diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinSafeCallOperatorFilterTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinSafeCallOperatorFilterTest.java
index 4a222a6ec7..27feed28eb 100644
--- a/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinSafeCallOperatorFilterTest.java
+++ b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinSafeCallOperatorFilterTest.java
@@ -89,7 +89,7 @@ public void should_filter_optimized_safe_call_chain() {
* fun example(a: A?): String? =
* a
* ?.b
- * ?.c
+ * ?.c // line 6
*
*/
@Test
@@ -108,6 +108,9 @@ public void should_filter_unoptimized_safe_call_chain() {
m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "A", "getB", "()LB;", false);
m.visitVarInsn(Opcodes.ASTORE, 1);
+ final Label lineNumberLabel = new Label();
+ m.visitLabel(lineNumberLabel);
+ m.visitLineNumber(6, lineNumberLabel);
m.visitVarInsn(Opcodes.ALOAD, 1);
m.visitJumpInsn(Opcodes.IFNULL, label1);
final AbstractInsnNode ifNullInstruction2 = m.instructions.getLast();
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinSafeCallOperatorFilter.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinSafeCallOperatorFilter.java
index 8fce63c698..e268720913 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinSafeCallOperatorFilter.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinSafeCallOperatorFilter.java
@@ -108,12 +108,12 @@ private static Collection> findChains(
continue;
}
} else if (target.getOpcode() == Opcodes.ACONST_NULL) {
- if (i.getPrevious().getOpcode() != Opcodes.ALOAD) {
+ final AbstractInsnNode p1 = preceding(i);
+ if (p1.getOpcode() != Opcodes.ALOAD) {
continue;
}
if (chain != null) {
- final AbstractInsnNode p1 = i.getPrevious();
- final AbstractInsnNode p2 = p1.getPrevious();
+ final AbstractInsnNode p2 = preceding(p1);
if (p2 == null || p2.getOpcode() != Opcodes.ASTORE
|| ((VarInsnNode) p1).var != ((VarInsnNode) p2).var) {
continue;
@@ -131,4 +131,19 @@ private static Collection> findChains(
return chains.values();
}
+ /**
+ * @return non pseudo-instruction preceding given
+ */
+ private static AbstractInsnNode preceding(AbstractInsnNode i) {
+ if (i == null) {
+ return null;
+ }
+ do {
+ i = i.getPrevious();
+ } while (i != null && (i.getType() == AbstractInsnNode.LABEL
+ || i.getType() == AbstractInsnNode.LINE
+ || i.getType() == AbstractInsnNode.FRAME));
+ return i;
+ }
+
}
diff --git a/org.jacoco.doc/docroot/doc/changes.html b/org.jacoco.doc/docroot/doc/changes.html
index 3af3388bdc..c1149d03d3 100644
--- a/org.jacoco.doc/docroot/doc/changes.html
+++ b/org.jacoco.doc/docroot/doc/changes.html
@@ -32,6 +32,9 @@
New Features
Part of bytecode generated by the Kotlin compiler for elvis operator that
follows safe call operator is filtered out during generation of report
(GitHub #1814).
+
Part of bytecode generated by the Kotlin compiler for more cases of chained
+ safe call operators is filtered out during generation of report
+ (GitHub #1956).
Part of bytecode generated by the Kotlin compiler for elvis operator that
follows safe call operator is filtered out during generation of report
- (GitHub #1814).
Part of bytecode generated by the Kotlin compiler for more cases of chained
safe call operators is filtered out during generation of report
(GitHub #1956).
From c2fe5cc54472f188c0d0f1158a0f987295217138 Mon Sep 17 00:00:00 2001
From: Ed Merks
Date: Tue, 7 Oct 2025 01:58:04 +0200
Subject: [PATCH 249/255] Upgrade ASM to 9.9 (#1965)
---
org.jacoco.build/licenses/{asm-9.8.html => asm-9.9.html} | 2 +-
org.jacoco.build/pom.xml | 2 +-
org.jacoco.doc/docroot/doc/changes.html | 6 ++++++
3 files changed, 8 insertions(+), 2 deletions(-)
rename org.jacoco.build/licenses/{asm-9.8.html => asm-9.9.html} (96%)
diff --git a/org.jacoco.build/licenses/asm-9.8.html b/org.jacoco.build/licenses/asm-9.9.html
similarity index 96%
rename from org.jacoco.build/licenses/asm-9.8.html
rename to org.jacoco.build/licenses/asm-9.9.html
index f74f83dc13..fb2619b4d5 100644
--- a/org.jacoco.build/licenses/asm-9.8.html
+++ b/org.jacoco.build/licenses/asm-9.9.html
@@ -1,7 +1,7 @@
ASM
- ASM 9.8 is subject to the terms and
+ ASM 9.9 is subject to the terms and
conditions of the following license:
From de76181b207b18c5b727051f8d62c115dc2c976c Mon Sep 17 00:00:00 2001
From: Evgeny Mandrikov <138671+Godin@users.noreply.github.com>
Date: Sat, 11 Oct 2025 01:31:58 +0200
Subject: [PATCH 254/255] KotlinSerializableFilter should filter more methods
(#1971)
---
.../kotlin/KotlinSerializableEnumTest.java | 6 +
.../kotlin/KotlinSerializableObjectTest.java | 33 ++++
.../kotlin/KotlinSerializableSealedTest.java | 16 ++
.../kotlin/KotlinSerializableTest.java | 9 ++
.../targets/KotlinSerializableEnumTarget.kt | 8 +-
.../targets/KotlinSerializableObjectTarget.kt | 31 ++++
.../targets/KotlinSerializableSealedTarget.kt | 12 +-
.../filter/KotlinSerializableFilterTest.java | 143 ++++++++++++++++--
.../filter/KotlinSerializableFilter.java | 53 +++++--
org.jacoco.doc/docroot/doc/changes.html | 3 +-
10 files changed, 276 insertions(+), 38 deletions(-)
create mode 100644 org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinSerializableObjectTest.java
create mode 100644 org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinSerializableObjectTarget.kt
diff --git a/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinSerializableEnumTest.java b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinSerializableEnumTest.java
index bc2508690c..4b32bee0d2 100644
--- a/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinSerializableEnumTest.java
+++ b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinSerializableEnumTest.java
@@ -14,6 +14,7 @@
import org.jacoco.core.test.validation.ValidationTestBase;
import org.jacoco.core.test.validation.kotlin.targets.KotlinSerializableEnumTarget;
+import org.junit.Test;
/**
* Test of code coverage in {@link KotlinSerializableEnumTarget}.
@@ -24,4 +25,9 @@ public KotlinSerializableEnumTest() {
super(KotlinSerializableEnumTarget.class);
}
+ @Test
+ public void test_method_count() {
+ assertMethodCount(/* main + static initializer */2);
+ }
+
}
diff --git a/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinSerializableObjectTest.java b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinSerializableObjectTest.java
new file mode 100644
index 0000000000..5d42a805b2
--- /dev/null
+++ b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinSerializableObjectTest.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2025 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.KotlinSerializableObjectTarget;
+import org.junit.Test;
+
+/**
+ * Test of code coverage in {@link KotlinSerializableObjectTarget}.
+ */
+public class KotlinSerializableObjectTest extends ValidationTestBase {
+
+ public KotlinSerializableObjectTest() {
+ super(KotlinSerializableObjectTarget.class);
+ }
+
+ @Test
+ public void test_method_count() {
+ assertMethodCount(/* main + static initializer */2);
+ }
+
+}
diff --git a/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinSerializableSealedTest.java b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinSerializableSealedTest.java
index d225d9a374..a4374ca2d3 100644
--- a/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinSerializableSealedTest.java
+++ b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinSerializableSealedTest.java
@@ -12,8 +12,12 @@
*******************************************************************************/
package org.jacoco.core.test.validation.kotlin;
+import java.util.Collection;
+import java.util.Collections;
+
import org.jacoco.core.test.validation.ValidationTestBase;
import org.jacoco.core.test.validation.kotlin.targets.KotlinSerializableSealedTarget;
+import org.junit.Test;
/**
* Test of code coverage in {@link KotlinSerializableSealedTarget}.
@@ -24,4 +28,16 @@ public KotlinSerializableSealedTest() {
super(KotlinSerializableSealedTarget.class);
}
+ @Override
+ protected Collection additionalClassesForAnalysis() {
+ return Collections.singletonList(
+ "org.jacoco.core.test.validation.kotlin.targets.KotlinSerializableSealedTarget$Sealed$A$$serializer");
+ }
+
+ @Test
+ public void test_method_count() {
+ assertMethodCount(
+ /* main + static initializer + constructor + getter */4);
+ }
+
}
diff --git a/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinSerializableTest.java b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinSerializableTest.java
index 1afc08cf5a..e6131254a6 100644
--- a/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinSerializableTest.java
+++ b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/KotlinSerializableTest.java
@@ -12,6 +12,9 @@
*******************************************************************************/
package org.jacoco.core.test.validation.kotlin;
+import java.util.Collection;
+import java.util.Collections;
+
import org.jacoco.core.test.validation.ValidationTestBase;
import org.jacoco.core.test.validation.kotlin.targets.KotlinSerializableTarget;
import org.junit.Test;
@@ -25,6 +28,12 @@ public KotlinSerializableTest() {
super(KotlinSerializableTarget.class);
}
+ @Override
+ protected Collection additionalClassesForAnalysis() {
+ return Collections.singletonList(
+ "org.jacoco.core.test.validation.kotlin.targets.KotlinSerializableTarget$Example$$serializer");
+ }
+
@Test
public void test_method_count() {
assertMethodCount(
diff --git a/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinSerializableEnumTarget.kt b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinSerializableEnumTarget.kt
index de3dae92a1..5cad0e605a 100644
--- a/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinSerializableEnumTarget.kt
+++ b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinSerializableEnumTarget.kt
@@ -19,10 +19,10 @@ import kotlinx.serialization.Serializable
*/
object KotlinSerializableEnumTarget {
- @Serializable
- enum class E {
- V
- }
+ @Serializable // assertFullyCovered()
+ enum class E { // assertEmpty()
+ V // assertFullyCovered()
+ } // assertFullyCovered()
@JvmStatic
fun main(args: Array) {
diff --git a/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinSerializableObjectTarget.kt b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinSerializableObjectTarget.kt
new file mode 100644
index 0000000000..72effef81c
--- /dev/null
+++ b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinSerializableObjectTarget.kt
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2025 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 kotlinx.serialization.Serializable
+
+/**
+ * Test target with [Serializable] `object`.
+ */
+object KotlinSerializableObjectTarget {
+
+ @Serializable // assertFullyCovered()
+ private object Example { // assertEmpty()
+ } // assertFullyCovered()
+
+ @JvmStatic
+ fun main(args: Array) {
+ Example.toString()
+ }
+
+}
diff --git a/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinSerializableSealedTarget.kt b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinSerializableSealedTarget.kt
index 0e639ab198..ce04755339 100644
--- a/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinSerializableSealedTarget.kt
+++ b/org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinSerializableSealedTarget.kt
@@ -19,11 +19,13 @@ import kotlinx.serialization.Serializable
*/
object KotlinSerializableSealedTarget {
- @Serializable
- private sealed class Sealed {
- @Serializable
- data class A(val data: String): Sealed()
- }
+ @Serializable // assertFullyCovered()
+ private sealed class Sealed { // assertEmpty()
+ @Serializable // assertFullyCovered()
+ data class A( // assertFullyCovered()
+ val data: String // assertFullyCovered()
+ ): Sealed() // assertEmpty()
+ } // assertFullyCovered()
@JvmStatic
fun main(args: Array) {
diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinSerializableFilterTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinSerializableFilterTest.java
index ab678e75cd..6e626511f0 100644
--- a/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinSerializableFilterTest.java
+++ b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinSerializableFilterTest.java
@@ -172,6 +172,15 @@ public void should_not_filter_hand_written_serializer_method() {
}
/**
+ * Example.serializer in case of
+ *
+ *
+ * @kotlinx.serialization.Serializable // line 1
+ * object Example
+ *
+ *
+ * Example$Companion.serializer in case of
+ *
*
* @kotlinx.serialization.Serializable // line 1
* enum class Example {
@@ -181,20 +190,12 @@ public void should_not_filter_hand_written_serializer_method() {
*
*
* @kotlinx.serialization.Serializable // line 1
- * sealed class Example {
- * }
+ * sealed class Example
*
*/
@Test
- public void should_filter_generated_serializer_method_in_companions_of_enum_and_sealed_class() {
- context.className = "Example$Companion";
-
- final MethodNode initMethod = new MethodNode(Opcodes.ACC_PRIVATE,
- "", "()V", null, null);
- final Label initMethodLineNumberLabel = new Label();
- initMethod.visitLabel(initMethodLineNumberLabel);
- initMethod.visitLineNumber(1, initMethodLineNumberLabel);
- filter.filter(initMethod, context, output);
+ public void should_filter_generated_serializer_method_in_objects_and_companions_of_enum_and_sealed_class() {
+ context.className = "Example";
final MethodNode m = new MethodNode(
Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL, "serializer",
@@ -204,15 +205,129 @@ public void should_filter_generated_serializer_method_in_companions_of_enum_and_
m.visitLabel(label0);
m.visitLineNumber(1, label0);
m.visitVarInsn(Opcodes.ALOAD, 0);
- m.visitMethodInsn(Opcodes.INVOKESPECIAL, "Example$Companion",
+ m.visitMethodInsn(Opcodes.INVOKESPECIAL, "Example",
"get$cachedSerializer", "()Lkotlinx/serialization/KSerializer;",
false);
m.visitInsn(Opcodes.ARETURN);
filter.filter(m, context, output);
- // FIXME https://github.com/jacoco/jacoco/issues/1971
- assertIgnored(m);
+ assertMethodIgnored(m);
+ }
+
+ /**
+ * Example.get$cachedSerializer in case of
+ *
+ *
+ * @kotlinx.serialization.Serializable
+ * object Example
+ *
+ *
+ * Example$Companion.get$cachedSerializer in case of
+ *
+ *
+ * @kotlinx.serialization.Serializable
+ * enum class Example {
+ * V
+ * }
+ *
+ *
+ *
+ * @kotlinx.serialization.Serializable
+ * sealed class Example
+ *