aboutsummaryrefslogtreecommitdiff
path: root/org.jacoco.core/src
diff options
context:
space:
mode:
authorEvgeny Mandrikov <Godin@users.noreply.github.com>2018-08-17 21:37:09 +0200
committerMarc R. Hoffmann <hoffmann@mountainminds.com>2018-08-17 21:37:09 +0200
commit4741fb65cbebd799fece1c36ebc131cb0945a159 (patch)
tree187192826f221e3e92995e6408948e0bfc9bee0d /org.jacoco.core/src
parent3208aad8a26528558fafe5f1b44f9d1525c1c9d0 (diff)
downloadjacoco-4741fb65cbebd799fece1c36ebc131cb0945a159.tar.gz
Add filter for bytecode that ECJ generates for String in switch (#735)
Diffstat (limited to 'org.jacoco.core/src')
-rw-r--r--org.jacoco.core/src/org/jacoco/core/internal/analysis/MethodAnalyzer.java28
-rw-r--r--org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/Filters.java7
-rw-r--r--org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/IFilterOutput.java14
-rw-r--r--org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/StringSwitchEcjFilter.java108
4 files changed, 152 insertions, 5 deletions
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/MethodAnalyzer.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/MethodAnalyzer.java
index ba862adc..82b97466 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/MethodAnalyzer.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/MethodAnalyzer.java
@@ -173,6 +173,13 @@ public class MethodAnalyzer extends MethodProbesVisitor
}
}
+ private final Map<AbstractInsnNode, Set<AbstractInsnNode>> replacements = new HashMap<AbstractInsnNode, Set<AbstractInsnNode>>();
+
+ public void replaceBranches(final AbstractInsnNode source,
+ final Set<AbstractInsnNode> newTargets) {
+ replacements.put(source, newTargets);
+ }
+
@Override
public void visitLabel(final Label label) {
currentLabel.add(label);
@@ -362,6 +369,7 @@ public class MethodAnalyzer extends MethodProbesVisitor
for (final CoveredProbe p : coveredProbes) {
p.instruction.setCovered(p.branch);
}
+
// Merge:
for (final Instruction i : instructions) {
final AbstractInsnNode m = i.getNode();
@@ -371,6 +379,7 @@ public class MethodAnalyzer extends MethodProbesVisitor
nodeToInstruction.get(r).merge(i);
}
}
+
// Report result:
coverage.ensureCapacity(firstLine, lastLine);
for (final Instruction i : instructions) {
@@ -378,8 +387,23 @@ public class MethodAnalyzer extends MethodProbesVisitor
continue;
}
- final int total = i.getBranches();
- final int covered = i.getCoveredBranches();
+ final int total;
+ final int covered;
+ final Set<AbstractInsnNode> r = replacements.get(i.getNode());
+ if (r != null) {
+ int cb = 0;
+ for (AbstractInsnNode b : r) {
+ if (nodeToInstruction.get(b).getCoveredBranches() > 0) {
+ cb++;
+ }
+ }
+ total = r.size();
+ covered = cb;
+ } else {
+ total = i.getBranches();
+ covered = i.getCoveredBranches();
+ }
+
final ICounter instrCounter = covered == 0 ? CounterImpl.COUNTER_1_0
: CounterImpl.COUNTER_0_1;
final ICounter branchCounter = total > 1
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 b235cfb8..bdb0854b 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
@@ -31,9 +31,10 @@ public final class Filters implements IFilter {
new TryWithResourcesJavac11Filter(),
new TryWithResourcesJavacFilter(), new TryWithResourcesEcjFilter(),
new FinallyFilter(), new PrivateEmptyNoArgConstructorFilter(),
- new StringSwitchJavacFilter(), new EnumEmptyConstructorFilter(),
- new AnnotationGeneratedFilter(), new KotlinGeneratedFilter(),
- new KotlinLateinitFilter(), new KotlinWhenSealedFilter());
+ new StringSwitchJavacFilter(), new StringSwitchEcjFilter(),
+ new EnumEmptyConstructorFilter(), new AnnotationGeneratedFilter(),
+ new KotlinGeneratedFilter(), new KotlinLateinitFilter(),
+ new KotlinWhenSealedFilter());
private final IFilter[] filters;
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/IFilterOutput.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/IFilterOutput.java
index 4ca9b814..bbcbf00e 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/IFilterOutput.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/IFilterOutput.java
@@ -11,6 +11,8 @@
*******************************************************************************/
package org.jacoco.core.internal.analysis.filter;
+import java.util.Set;
+
import org.objectweb.asm.tree.AbstractInsnNode;
/**
@@ -41,4 +43,16 @@ public interface IFilterOutput {
*/
void merge(AbstractInsnNode i1, AbstractInsnNode i2);
+ /**
+ * Marks instruction whose outgoing branches should be replaced during
+ * computation of coverage.
+ *
+ * @param source
+ * instruction which branches should be replaced
+ * @param newTargets
+ * new targets of branches
+ */
+ void replaceBranches(AbstractInsnNode source,
+ Set<AbstractInsnNode> newTargets);
+
}
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/StringSwitchEcjFilter.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/StringSwitchEcjFilter.java
new file mode 100644
index 00000000..cbd2a216
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/StringSwitchEcjFilter.java
@@ -0,0 +1,108 @@
+/*******************************************************************************
+ * 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 code that is generated by ECJ for a <code>switch</code> statement
+ * with a <code>String</code>.
+ */
+public final class StringSwitchEcjFilter 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");
+ 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 case
+ nextIs(Opcodes.IFNE);
+ if (cursor == null) {
+ return;
+ }
+
+ replacements.add(instructionAfterLabel(
+ ((JumpInsnNode) cursor).label));
+
+ if (cursor.getNext().getOpcode() == Opcodes.GOTO) {
+ // end of comparisons for same hashCode
+ // jump to default
+ nextIs(Opcodes.GOTO);
+ 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;
+ }
+
+}