Skip to content

Commit

Permalink
Update filter for unsafe cast operator for Kotlin 1.4 (#1143)
Browse files Browse the repository at this point in the history
  • Loading branch information
Godin committed Jan 5, 2021
1 parent f72c2c8 commit e859274
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 13 deletions.
2 changes: 1 addition & 1 deletion org.jacoco.core.test.validation.kotlin/pom.xml
Expand Up @@ -25,7 +25,7 @@
<name>JaCoCo :: Test :: Core :: Validation Kotlin</name>

<properties>
<kotlin.version>1.3.61</kotlin.version>
<kotlin.version>1.4.0</kotlin.version>
</properties>

<dependencies>
Expand Down
Expand Up @@ -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");
Expand All @@ -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", "<init>",
"(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));
}

/**
* <pre>
* fun f(o: Any?) {
* if (o == null)
* throw NullPointerException("null cannot be cast to non-null type")
* }
* </pre>
*/
@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", "<init>",
"(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", "<init>",
"(Ljava/lang/String;)V", false);
m.visitInsn(Opcodes.ATHROW);
m.visitLabel(label);

filter.filter(m, context, output);

assertIgnored();
}

}
Expand Up @@ -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) {
Expand All @@ -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,
"<init>", "(Ljava/lang/String;)V");
nextIsInvoke(Opcodes.INVOKESPECIAL, exceptionType, "<init>",
"(Ljava/lang/String;)V");
nextIs(Opcodes.ATHROW);
if (cursor == null) {
return;
}
if (cursor.getNext() != ((JumpInsnNode) start).label) {
if (cursor.getNext() != jumpInsnNode.label) {
return;
}

Expand Down
3 changes: 3 additions & 0 deletions org.jacoco.doc/docroot/doc/changes.html
Expand Up @@ -27,6 +27,9 @@ <h3>New Features</h3>
<a href="https://github.com/jacoco/jacoco/issues/1097">#1097</a>).</li>
<li>Experimental support for Java 17 class files
(GitHub <a href="https://github.com/jacoco/jacoco/issues/1132">#1132</a>).</li>
<li>Branch added by the Kotlin compiler version 1.4.0 and above for "unsafe" cast
operator is filtered out during generation of report
(GitHub <a href="https://github.com/jacoco/jacoco/issues/1143">#1143</a>).</li>
</ul>

<h3>Non-functional Changes</h3>
Expand Down

0 comments on commit e859274

Please sign in to comment.