diff options
author | Evgeny Mandrikov <Godin@users.noreply.github.com> | 2018-08-18 08:01:29 +0200 |
---|---|---|
committer | Marc R. Hoffmann <hoffmann@mountainminds.com> | 2018-08-18 08:01:29 +0200 |
commit | 32073eafa35718ef7a95df979f2202282e1e4eb1 (patch) | |
tree | 6ac7e198947047ab78237f5c3a535450ece880d4 /org.jacoco.core/src/org/jacoco/core | |
parent | e629bf0fd2613ab887726800a3f935d3d51de71e (diff) | |
download | platform_external_jacoco-32073eafa35718ef7a95df979f2202282e1e4eb1.tar.gz platform_external_jacoco-32073eafa35718ef7a95df979f2202282e1e4eb1.tar.bz2 platform_external_jacoco-32073eafa35718ef7a95df979f2202282e1e4eb1.zip |
Add filter for Kotlin when-expressions with String (#737)
Diffstat (limited to 'org.jacoco.core/src/org/jacoco/core')
-rw-r--r-- | org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/Filters.java | 2 | ||||
-rw-r--r-- | org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinWhenStringFilter.java | 110 |
2 files changed, 111 insertions, 1 deletions
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/Filters.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/Filters.java index bdb0854b..a8446eac 100644 --- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/Filters.java +++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/Filters.java @@ -34,7 +34,7 @@ public final class Filters implements IFilter { new StringSwitchJavacFilter(), new StringSwitchEcjFilter(), new EnumEmptyConstructorFilter(), new AnnotationGeneratedFilter(), new KotlinGeneratedFilter(), new KotlinLateinitFilter(), - new KotlinWhenSealedFilter()); + new KotlinWhenSealedFilter(), new KotlinWhenStringFilter()); private final IFilter[] filters; diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinWhenStringFilter.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinWhenStringFilter.java new file mode 100644 index 00000000..861c7bfb --- /dev/null +++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinWhenStringFilter.java @@ -0,0 +1,110 @@ +/******************************************************************************* + * Copyright (c) 2009, 2018 Mountainminds GmbH & Co. KG and Contributors + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Evgeny Mandrikov - initial API and implementation + * + *******************************************************************************/ +package org.jacoco.core.internal.analysis.filter; + +import java.util.HashSet; +import java.util.Set; + +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.JumpInsnNode; +import org.objectweb.asm.tree.LabelNode; +import org.objectweb.asm.tree.LookupSwitchInsnNode; +import org.objectweb.asm.tree.MethodNode; +import org.objectweb.asm.tree.TableSwitchInsnNode; + +/** + * Filters bytecode that Kotlin compiler generates for <code>when</code> + * expressions with a <code>String</code>. + */ +public final class KotlinWhenStringFilter implements IFilter { + + public void filter(final MethodNode methodNode, + final IFilterContext context, final IFilterOutput output) { + final Matcher matcher = new Matcher(); + for (AbstractInsnNode i = methodNode.instructions + .getFirst(); i != null; i = i.getNext()) { + matcher.match(i, output); + } + } + + private static class Matcher extends AbstractMatcher { + public void match(final AbstractInsnNode start, + final IFilterOutput output) { + + cursor = start; + + nextIsVar(Opcodes.ASTORE, "s"); + nextIsVar(Opcodes.ALOAD, "s"); + nextIsInvokeVirtual("java/lang/String", "hashCode"); + nextIsSwitch(); + if (cursor == null) { + return; + } + + final AbstractInsnNode s = cursor; + final int hashCodes; + final LabelNode defaultLabel; + if (s.getOpcode() == Opcodes.LOOKUPSWITCH) { + final LookupSwitchInsnNode lookupSwitch = (LookupSwitchInsnNode) cursor; + defaultLabel = lookupSwitch.dflt; + hashCodes = lookupSwitch.labels.size(); + } else { + final TableSwitchInsnNode tableSwitch = (TableSwitchInsnNode) cursor; + defaultLabel = tableSwitch.dflt; + hashCodes = tableSwitch.labels.size(); + } + + final Set<AbstractInsnNode> replacements = new HashSet<AbstractInsnNode>(); + replacements.add(instructionAfterLabel(defaultLabel)); + + for (int i = 0; i < hashCodes; i++) { + while (true) { + nextIsVar(Opcodes.ALOAD, "s"); + nextIs(Opcodes.LDC); + nextIsInvokeVirtual("java/lang/String", "equals"); + // jump to next comparison or default case + nextIs(Opcodes.IFEQ); + final JumpInsnNode jump = (JumpInsnNode) cursor; + // jump to case + nextIs(Opcodes.GOTO); + if (cursor == null) { + return; + } + + replacements.add(instructionAfterLabel( + ((JumpInsnNode) cursor).label)); + + if (jump.label == defaultLabel) { + // end of comparisons for same hashCode + break; + } + } + } + + output.ignore(s.getNext(), cursor); + output.replaceBranches(s, replacements); + } + } + + private static AbstractInsnNode instructionAfterLabel( + final LabelNode label) { + AbstractInsnNode i = label.getNext(); + while (i.getType() == AbstractInsnNode.FRAME + || i.getType() == AbstractInsnNode.LABEL + || i.getType() == AbstractInsnNode.LINE) { + i = i.getNext(); + } + return i; + } + +} |