Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update filter for unsafe cast operator for Kotlin 1.4 #1143

Merged
merged 9 commits into from Jan 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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