From e859274c57f4bd28cbe34d3048682e5ab74e7a3d Mon Sep 17 00:00:00 2001 From: Evgeny Mandrikov <138671+Godin@users.noreply.github.com> Date: Tue, 5 Jan 2021 19:37:50 +0100 Subject: [PATCH] Update filter for unsafe cast operator for Kotlin 1.4 (#1143) --- .../pom.xml | 2 +- .../KotlinUnsafeCastOperatorFilterTest.java | 77 ++++++++++++++++++- .../KotlinUnsafeCastOperatorFilter.java | 26 ++++--- org.jacoco.doc/docroot/doc/changes.html | 3 + 4 files changed, 95 insertions(+), 13 deletions(-) diff --git a/org.jacoco.core.test.validation.kotlin/pom.xml b/org.jacoco.core.test.validation.kotlin/pom.xml index 2705035613..69511f3a26 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 - 1.3.61 + 1.4.0 diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinUnsafeCastOperatorFilterTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinUnsafeCastOperatorFilterTest.java index 01f79b7671..71e376044d 100644 --- a/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinUnsafeCastOperatorFilterTest.java +++ b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinUnsafeCastOperatorFilterTest.java @@ -31,11 +31,13 @@ public class KotlinUnsafeCastOperatorFilterTest extends FilterTestBase { @Test public void should_filter() { + context.classAnnotations + .add(KotlinGeneratedFilter.KOTLIN_METADATA_DESC); final Label label = new Label(); m.visitInsn(Opcodes.DUP); - m.visitJumpInsn(Opcodes.IFNONNULL, label); final AbstractInsnNode expectedFrom = m.instructions.getLast(); + m.visitJumpInsn(Opcodes.IFNONNULL, label); m.visitTypeInsn(Opcodes.NEW, "kotlin/TypeCastException"); m.visitInsn(Opcodes.DUP); m.visitLdcInsn("null cannot be cast to non-null type kotlin.String"); @@ -50,4 +52,77 @@ public void should_filter() { assertIgnored(new Range(expectedFrom, expectedTo)); } + @Test + public void should_filter_Kotlin_1_4() { + context.classAnnotations + .add(KotlinGeneratedFilter.KOTLIN_METADATA_DESC); + final Label label = new Label(); + + m.visitInsn(Opcodes.DUP); + final AbstractInsnNode expectedFrom = m.instructions.getLast(); + m.visitJumpInsn(Opcodes.IFNONNULL, label); + m.visitTypeInsn(Opcodes.NEW, "java/lang/NullPointerException"); + m.visitInsn(Opcodes.DUP); + m.visitLdcInsn("null cannot be cast to non-null type kotlin.String"); + m.visitMethodInsn(Opcodes.INVOKESPECIAL, + "java/lang/NullPointerException", "", + "(Ljava/lang/String;)V", false); + m.visitInsn(Opcodes.ATHROW); + final AbstractInsnNode expectedTo = m.instructions.getLast(); + m.visitLabel(label); + + filter.filter(m, context, output); + + assertIgnored(new Range(expectedFrom, expectedTo)); + } + + /** + *
+	 *   fun f(o: Any?) {
+	 *     if (o == null)
+	 *       throw NullPointerException("null cannot be cast to non-null type")
+	 *   }
+	 * 
+ */ + @Test + public void should_not_filter() { + context.classAnnotations + .add(KotlinGeneratedFilter.KOTLIN_METADATA_DESC); + m.visitVarInsn(Opcodes.ALOAD, 1); + final Label label = new Label(); + m.visitJumpInsn(Opcodes.IFNONNULL, label); + m.visitTypeInsn(Opcodes.NEW, "java/lang/NullPointerException"); + m.visitInsn(Opcodes.DUP); + m.visitLdcInsn("null cannot be cast to non-null type"); + m.visitMethodInsn(Opcodes.INVOKESPECIAL, + "java/lang/NullPointerException", "", + "(Ljava/lang/String;)V", false); + m.visitInsn(Opcodes.ATHROW); + m.visitLabel(label); + m.visitInsn(Opcodes.RETURN); + + filter.filter(m, context, output); + + assertIgnored(); + } + + @Test + public void should_not_filter_when_not_kotlin() { + m.visitInsn(Opcodes.DUP); + final Label label = new Label(); + m.visitJumpInsn(Opcodes.IFNONNULL, label); + m.visitTypeInsn(Opcodes.NEW, "java/lang/NullPointerException"); + m.visitInsn(Opcodes.DUP); + m.visitLdcInsn("null cannot be cast to non-null type kotlin.String"); + m.visitMethodInsn(Opcodes.INVOKESPECIAL, + "java/lang/NullPointerException", "", + "(Ljava/lang/String;)V", false); + m.visitInsn(Opcodes.ATHROW); + m.visitLabel(label); + + filter.filter(m, context, output); + + assertIgnored(); + } + } 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 3bfc27b0e5..f4aaa7f145 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 @@ -24,26 +24,30 @@ */ public final class KotlinUnsafeCastOperatorFilter implements IFilter { - private static final String KOTLIN_TYPE_CAST_EXCEPTION = "kotlin/TypeCastException"; - public void filter(final MethodNode methodNode, final IFilterContext context, final IFilterOutput output) { + if (!KotlinGeneratedFilter.isKotlinClass(context)) { + return; + } final Matcher matcher = new Matcher(); for (final AbstractInsnNode i : methodNode.instructions) { - matcher.match(i, output); + matcher.match("kotlin/TypeCastException", i, output); + // Since Kotlin 1.4.0: + matcher.match("java/lang/NullPointerException", i, output); } } private static class Matcher extends AbstractMatcher { - public void match(final AbstractInsnNode start, - final IFilterOutput output) { + public void match(final String exceptionType, + final AbstractInsnNode start, final IFilterOutput output) { - if (Opcodes.IFNONNULL != start.getOpcode()) { + if (Opcodes.DUP != start.getOpcode()) { return; } cursor = start; - - nextIsType(Opcodes.NEW, KOTLIN_TYPE_CAST_EXCEPTION); + nextIs(Opcodes.IFNONNULL); + final JumpInsnNode jumpInsnNode = (JumpInsnNode) cursor; + nextIsType(Opcodes.NEW, exceptionType); nextIs(Opcodes.DUP); nextIs(Opcodes.LDC); if (cursor == null) { @@ -54,13 +58,13 @@ public void match(final AbstractInsnNode start, .startsWith("null cannot be cast to non-null type"))) { return; } - nextIsInvoke(Opcodes.INVOKESPECIAL, KOTLIN_TYPE_CAST_EXCEPTION, - "", "(Ljava/lang/String;)V"); + nextIsInvoke(Opcodes.INVOKESPECIAL, exceptionType, "", + "(Ljava/lang/String;)V"); nextIs(Opcodes.ATHROW); if (cursor == null) { return; } - if (cursor.getNext() != ((JumpInsnNode) start).label) { + if (cursor.getNext() != jumpInsnNode.label) { return; } diff --git a/org.jacoco.doc/docroot/doc/changes.html b/org.jacoco.doc/docroot/doc/changes.html index a75d1f4daa..5d20c4a687 100644 --- a/org.jacoco.doc/docroot/doc/changes.html +++ b/org.jacoco.doc/docroot/doc/changes.html @@ -27,6 +27,9 @@

New Features

#1097).
  • Experimental support for Java 17 class files (GitHub #1132).
  • +
  • Branch added by the Kotlin compiler version 1.4.0 and above for "unsafe" cast + operator is filtered out during generation of report + (GitHub #1143).
  • Non-functional Changes