Skip to content

Commit 71f9341

Browse files
authored
Update filter for suspending functions with tail call optimization (bazel-contrib#1016)
1 parent 04fe200 commit 71f9341

File tree

5 files changed

+105
-2
lines changed

5 files changed

+105
-2
lines changed

org.jacoco.core.test.validation.kotlin/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626

2727
<properties>
2828
<bytecode.version>6</bytecode.version>
29-
<kotlin.version>1.3.31</kotlin.version>
29+
<kotlin.version>1.3.61</kotlin.version>
3030
</properties>
3131

3232
<dependencies>

org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinCoroutineTarget.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ object KotlinCoroutineTarget {
2525
nop() // assertFullyCovered()
2626
} // assertFullyCovered()
2727

28+
private suspend fun suspendingFunctionWithTailCallOptimization() { // assertEmpty()
29+
nop() // assertFullyCovered()
30+
anotherSuspendingFunction() // assertFullyCovered()
31+
} // assertFullyCovered()
32+
2833
private suspend fun anotherSuspendingFunction() {
2934
nop() // assertFullyCovered()
3035
}
@@ -37,6 +42,7 @@ object KotlinCoroutineTarget {
3742
nop(x) // assertFullyCovered()
3843
suspendingFunction() // assertFullyCovered()
3944
nop(x) // assertFullyCovered()
45+
suspendingFunctionWithTailCallOptimization()
4046
} // assertFullyCovered()
4147

4248
}

org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinCoroutineFilterTest.java

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,4 +374,80 @@ public void should_filter_suspending_functions() {
374374
assertIgnored(range0, range1, range2);
375375
}
376376

377+
/**
378+
* <pre>
379+
* suspend fun example(b: Boolean) {
380+
* if (b)
381+
* suspendingFunction()
382+
* else
383+
* suspendingFunction()
384+
* }
385+
* </pre>
386+
*/
387+
@Test
388+
public void should_filter_suspending_functions_with_tail_call_optimization() {
389+
final MethodNode m = new MethodNode(InstrSupport.ASM_API_VERSION, 0,
390+
"example",
391+
"(ZLkotlin/coroutines/Continuation;)Ljava/lang/Object;", null,
392+
null);
393+
context.classAnnotations
394+
.add(KotlinGeneratedFilter.KOTLIN_METADATA_DESC);
395+
396+
final Label exit = new Label();
397+
398+
m.visitVarInsn(Opcodes.ILOAD, 1);
399+
final Label next = new Label();
400+
m.visitJumpInsn(Opcodes.IFEQ, next);
401+
402+
m.visitVarInsn(Opcodes.ALOAD, 0);
403+
m.visitVarInsn(Opcodes.ALOAD, 2);
404+
m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "", "suspendingFunction",
405+
"(Lkotlin/coroutines/Continuation;)Ljava/lang/Object;", false);
406+
final Range range1 = new Range();
407+
{
408+
m.visitInsn(Opcodes.DUP);
409+
range1.fromInclusive = m.instructions.getLast();
410+
m.visitMethodInsn(Opcodes.INVOKESTATIC,
411+
"kotlin/coroutines/intrinsics/IntrinsicsKt",
412+
"getCOROUTINE_SUSPENDED", "()Ljava/lang/Object;", false);
413+
final Label label = new Label();
414+
m.visitJumpInsn(Opcodes.IF_ACMPNE, label);
415+
m.visitInsn(Opcodes.ARETURN);
416+
m.visitLabel(label);
417+
m.visitInsn(Opcodes.POP);
418+
range1.toInclusive = m.instructions.getLast();
419+
}
420+
421+
m.visitJumpInsn(Opcodes.GOTO, exit);
422+
m.visitLabel(next);
423+
424+
m.visitVarInsn(Opcodes.ALOAD, 0);
425+
m.visitVarInsn(Opcodes.ALOAD, 2);
426+
m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "", "suspendingFunction",
427+
"(Lkotlin/coroutines/Continuation;)Ljava/lang/Object;", false);
428+
final Range range2 = new Range();
429+
{
430+
m.visitInsn(Opcodes.DUP);
431+
range2.fromInclusive = m.instructions.getLast();
432+
m.visitMethodInsn(Opcodes.INVOKESTATIC,
433+
"kotlin/coroutines/intrinsics/IntrinsicsKt",
434+
"getCOROUTINE_SUSPENDED", "()Ljava/lang/Object;", false);
435+
final Label label = new Label();
436+
m.visitJumpInsn(Opcodes.IF_ACMPNE, label);
437+
m.visitInsn(Opcodes.ARETURN);
438+
m.visitLabel(label);
439+
m.visitInsn(Opcodes.POP);
440+
range2.toInclusive = m.instructions.getLast();
441+
}
442+
443+
m.visitLabel(exit);
444+
m.visitFieldInsn(Opcodes.GETSTATIC, "kotlin/Unit", "INSTANCE",
445+
"Lkotlin/Unit;");
446+
m.visitInsn(Opcodes.ARETURN);
447+
448+
filter.filter(m, context, output);
449+
450+
assertIgnored(range1, range2);
451+
}
452+
377453
}

org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinCoroutineFilter.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,28 @@ public void filter(final MethodNode methodNode,
4343
}
4444

4545
new Matcher().match(methodNode, output);
46-
46+
new Matcher().matchOptimizedTailCall(methodNode, output);
4747
}
4848

4949
private static class Matcher extends AbstractMatcher {
50+
51+
private void matchOptimizedTailCall(final MethodNode methodNode,
52+
final IFilterOutput output) {
53+
for (final AbstractInsnNode i : methodNode.instructions) {
54+
cursor = i;
55+
nextIs(Opcodes.DUP);
56+
nextIsInvoke(Opcodes.INVOKESTATIC,
57+
"kotlin/coroutines/intrinsics/IntrinsicsKt",
58+
"getCOROUTINE_SUSPENDED", "()Ljava/lang/Object;");
59+
nextIs(Opcodes.IF_ACMPNE);
60+
nextIs(Opcodes.ARETURN);
61+
nextIs(Opcodes.POP);
62+
if (cursor != null) {
63+
output.ignore(i.getNext(), cursor);
64+
}
65+
}
66+
}
67+
5068
private void match(final MethodNode methodNode,
5169
final IFilterOutput output) {
5270
cursor = methodNode.instructions.getFirst();

org.jacoco.doc/docroot/doc/changes.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ <h3>New Features</h3>
3232
<li>Methods generated by Kotlin compiler for non-overridden non-abstract methods
3333
of interfaces are filtered out during generation of report
3434
(GitHub <a href="https://github.com/jacoco/jacoco/issues/1012">#1012</a>).</li>
35+
<li>Branches added by the Kotlin compiler version 1.3.60 for suspending functions
36+
with tail call optimization are filtered out during generation of report
37+
(GitHub <a href="https://github.com/jacoco/jacoco/issues/1016">#1016</a>).</li>
3538
</ul>
3639

3740
<h3>Non-functional Changes</h3>

0 commit comments

Comments
 (0)