From 3b8df21b64ec1e581c620b7a12483d7a6ee1655b Mon Sep 17 00:00:00 2001 From: Evgeny Mandrikov Date: Wed, 26 Dec 2018 07:43:03 +0100 Subject: Update filter for Kotlin coroutines that restore state (#803) --- .../internal/analysis/filter/AbstractMatcher.java | 10 +++++ .../analysis/filter/KotlinCoroutineFilter.java | 52 ++++++++++++++-------- 2 files changed, 43 insertions(+), 19 deletions(-) (limited to 'org.jacoco.core/src') diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/AbstractMatcher.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/AbstractMatcher.java index 2ae1499b..ca7fb9ac 100644 --- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/AbstractMatcher.java +++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/AbstractMatcher.java @@ -158,11 +158,21 @@ abstract class AbstractMatcher { * {@link AbstractInsnNode#LABEL}, {@link AbstractInsnNode#LINE}. */ final void skipNonOpcodes() { + cursor = skipNonOpcodes(cursor); + } + + /** + * Returns first instruction from given and following it that is not + * {@link AbstractInsnNode#FRAME}, {@link AbstractInsnNode#LABEL}, + * {@link AbstractInsnNode#LINE}. + */ + static AbstractInsnNode skipNonOpcodes(AbstractInsnNode cursor) { while (cursor != null && (cursor.getType() == AbstractInsnNode.FRAME || cursor.getType() == AbstractInsnNode.LABEL || cursor.getType() == AbstractInsnNode.LINE)) { cursor = cursor.getNext(); } + return cursor; } } diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinCoroutineFilter.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinCoroutineFilter.java index f2ecd340..f1261b22 100644 --- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinCoroutineFilter.java +++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinCoroutineFilter.java @@ -16,6 +16,7 @@ import java.util.List; import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.JumpInsnNode; import org.objectweb.asm.tree.LdcInsnNode; import org.objectweb.asm.tree.MethodNode; import org.objectweb.asm.tree.TableSwitchInsnNode; @@ -72,32 +73,45 @@ public final class KotlinCoroutineFilter implements IFilter { ignore.add(s); ignore.add(cursor); - for (AbstractInsnNode i = methodNode.instructions - .getFirst(); i != null; i = i.getNext()) { + int suspensionPoint = 1; + for (AbstractInsnNode i = cursor; i != null + && suspensionPoint < s.labels.size(); i = i.getNext()) { cursor = i; nextIsVar(Opcodes.ALOAD, "COROUTINE_SUSPENDED"); nextIs(Opcodes.IF_ACMPNE); + if (cursor == null) { + continue; + } + final AbstractInsnNode continuationAfterLoadedResult = skipNonOpcodes( + (((JumpInsnNode) cursor)).label); nextIsVar(Opcodes.ALOAD, "COROUTINE_SUSPENDED"); nextIs(Opcodes.ARETURN); - - 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); - - nextIs(Opcodes.ALOAD); - if (cursor != null) { - ignore.add(i); - ignore.add(cursor); + if (cursor == null + || skipNonOpcodes(cursor.getNext()) != skipNonOpcodes( + s.labels.get(suspensionPoint))) { + continue; } - } - if (ignore.size() != s.labels.size() * 2) { - return; + 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); + + nextIs(Opcodes.ALOAD); + if (cursor != null && skipNonOpcodes(cursor + .getNext()) == continuationAfterLoadedResult) { + ignore.add(i); + ignore.add(cursor); + suspensionPoint++; + break; + } + } } cursor = s.dflt; -- cgit v1.2.3