Skip to content

Commit

Permalink
Update KotlinCoroutineFilter for Kotlin 1.3.30 (#849)
Browse files Browse the repository at this point in the history
  • Loading branch information
Godin authored and marchof committed Mar 4, 2019
1 parent 5f46dcf commit c30eb29
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,92 @@ public class KotlinCoroutineFilterTest extends FilterTestBase {

private final IFilter filter = new KotlinCoroutineFilter();

@Test
public void should_filter_suspending_lambdas_generated_by_Kotlin_1_3_30() {
final MethodNode m = new MethodNode(InstrSupport.ASM_API_VERSION, 0,
"invokeSuspend", "(Ljava/lang/Object;)Ljava/lang/Object;", null,
null);
context.classAnnotations
.add(KotlinGeneratedFilter.KOTLIN_METADATA_DESC);

m.visitLabel(new Label());
final Range range1 = new Range();
range1.fromInclusive = m.instructions.getLast();
m.visitMethodInsn(Opcodes.INVOKESTATIC,
"kotlin/coroutines/intrinsics/IntrinsicsKt",
"getCOROUTINE_SUSPENDED", "()Ljava/lang/Object;", false);
m.visitVarInsn(Opcodes.ASTORE, 4);

m.visitVarInsn(Opcodes.ALOAD, 0);
// line of "runBlocking"
m.visitFieldInsn(Opcodes.GETFIELD, "Target", "label", "I");
final Label dflt = new Label();
final Label state0 = new Label();
final Label state1 = new Label();
m.visitTableSwitchInsn(0, 1, dflt, state0, state1);

m.visitLabel(state0);

{
m.visitVarInsn(Opcodes.ALOAD, 1);
m.visitMethodInsn(Opcodes.INVOKESTATIC, "kotlin/ResultKt",
"throwOnFailure", "", false);
range1.toInclusive = m.instructions.getLast();
}

// line before "suspendingFunction"
m.visitInsn(Opcodes.NOP);

// line of "suspendingFunction"
m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "", "suspendingFunction",
"(Lkotlin/coroutines/Continuation;)Ljava/lang/Object;", false);

m.visitInsn(Opcodes.DUP);
final Range range2 = new Range();
range2.fromInclusive = m.instructions.getLast();
m.visitVarInsn(Opcodes.ALOAD, 4);
final Label continuationLabelAfterLoadedResult = new Label();
m.visitJumpInsn(Opcodes.IF_ACMPNE, continuationLabelAfterLoadedResult);
// line of "runBlocking"
m.visitVarInsn(Opcodes.ALOAD, 4);
m.visitInsn(Opcodes.ARETURN);

m.visitLabel(state1);

m.visitVarInsn(Opcodes.ALOAD, 0);
m.visitFieldInsn(Opcodes.GETFIELD, "Target", "I$0", "I");
m.visitVarInsn(Opcodes.ISTORE, 3);

{
m.visitVarInsn(Opcodes.ALOAD, 1);
m.visitMethodInsn(Opcodes.INVOKESTATIC, "kotlin/ResultKt",
"throwOnFailure", "", false);
}
m.visitVarInsn(Opcodes.ALOAD, 1);
range2.toInclusive = m.instructions.getLast();
m.visitLabel(continuationLabelAfterLoadedResult);

// line after "suspendingFunction"
m.visitInsn(Opcodes.NOP);
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", "<init>",
"(Ljava/lang/String;)V", false);
m.visitInsn(Opcodes.ATHROW);
range0.toInclusive = m.instructions.getLast();

filter.filter(m, context, output);

assertIgnored(range0, range1, range2);
}

/**
* <pre>
* runBlocking {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,7 @@ private void match(final MethodNode methodNode,
s.labels.size() * 2);

nextIs(Opcodes.ALOAD);
nextIs(Opcodes.DUP);
nextIsType(Opcodes.INSTANCEOF, "kotlin/Result$Failure");
nextIs(Opcodes.IFEQ);
nextIsType(Opcodes.CHECKCAST, "kotlin/Result$Failure");
nextIs(Opcodes.GETFIELD);
nextIs(Opcodes.ATHROW);
nextIs(Opcodes.POP);
nextIsThrowOnFailure();

if (cursor == null) {
return;
Expand Down Expand Up @@ -109,13 +103,7 @@ private void match(final MethodNode methodNode,
for (AbstractInsnNode j = i; j != null; j = j.getNext()) {
cursor = j;
nextIs(Opcodes.ALOAD);
nextIs(Opcodes.DUP);
nextIsType(Opcodes.INSTANCEOF, "kotlin/Result$Failure");
nextIs(Opcodes.IFEQ);
nextIsType(Opcodes.CHECKCAST, "kotlin/Result$Failure");
nextIs(Opcodes.GETFIELD);
nextIs(Opcodes.ATHROW);
nextIs(Opcodes.POP);
nextIsThrowOnFailure();

nextIs(Opcodes.ALOAD);
if (cursor != null && skipNonOpcodes(cursor
Expand Down Expand Up @@ -149,6 +137,24 @@ private void match(final MethodNode methodNode,
}
}

private void nextIsThrowOnFailure() {
final AbstractInsnNode c = cursor;
nextIsInvokeStatic("kotlin/ResultKt", "throwOnFailure");
if (cursor == null) {
cursor = c;
// Before resolution of
// https://youtrack.jetbrains.com/issue/KT-28015 in
// Kotlin 1.3.30
nextIs(Opcodes.DUP);
nextIsType(Opcodes.INSTANCEOF, "kotlin/Result$Failure");
nextIs(Opcodes.IFEQ);
nextIsType(Opcodes.CHECKCAST, "kotlin/Result$Failure");
nextIs(Opcodes.GETFIELD);
nextIs(Opcodes.ATHROW);
nextIs(Opcodes.POP);
}
}

private void nextIsCreateStateInstance() {
nextIs(Opcodes.INSTANCEOF);

Expand Down
8 changes: 8 additions & 0 deletions org.jacoco.doc/docroot/doc/changes.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ <h1>Change History</h1>

<h2>Snapshot Build @qualified.bundle.version@ (@build.date@)</h2>

<h3>New Features</h3>

<ul>
<li>Branches added by the Kotlin compiler version 1.3.30 for suspending lambdas
and functions are filtered out during generation of report
(GitHub <a href="https://github.com/jacoco/jacoco/issues/849">#849</a>).</li>
</ul>

<h2>Release 0.8.3 (2019/01/23)</h2>

<h3>New Features</h3>
Expand Down

0 comments on commit c30eb29

Please sign in to comment.