aboutsummaryrefslogtreecommitdiff
path: root/org.jacoco.core/src/org/jacoco
diff options
context:
space:
mode:
authorEvgeny Mandrikov <Godin@users.noreply.github.com>2017-09-29 06:06:11 +0200
committerMarc R. Hoffmann <hoffmann@mountainminds.com>2017-09-29 06:06:11 +0200
commit1962cf59bc403a27f68c55b39bb833b00fa386a0 (patch)
tree2bca7259d0e9cfe50630f430b8a56a9bf3adbbc8 /org.jacoco.core/src/org/jacoco
parentaca2da1d7479d6b536e027e8fa7db74428d781e6 (diff)
downloadjacoco-1962cf59bc403a27f68c55b39bb833b00fa386a0.tar.gz
Yak shaving: add ability to merge coverage of several instructions (#601)
This is required for implementation of filter of duplicate blocks that compilers generate for `finally`.
Diffstat (limited to 'org.jacoco.core/src/org/jacoco')
-rw-r--r--org.jacoco.core/src/org/jacoco/core/internal/analysis/ClassAnalyzer.java3
-rw-r--r--org.jacoco.core/src/org/jacoco/core/internal/analysis/MethodAnalyzer.java75
-rw-r--r--org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/Filters.java48
-rw-r--r--org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/IFilterOutput.java11
-rw-r--r--org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/LombokGeneratedFilter.java2
-rw-r--r--org.jacoco.core/src/org/jacoco/core/internal/flow/Instruction.java11
6 files changed, 127 insertions, 23 deletions
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/ClassAnalyzer.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/ClassAnalyzer.java
index fa3517b6..69a10e7e 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/ClassAnalyzer.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/ClassAnalyzer.java
@@ -12,6 +12,7 @@
package org.jacoco.core.internal.analysis;
import org.jacoco.core.analysis.IMethodCoverage;
+import org.jacoco.core.internal.analysis.filter.Filters;
import org.jacoco.core.internal.flow.ClassProbesVisitor;
import org.jacoco.core.internal.flow.MethodProbesVisitor;
import org.jacoco.core.internal.instr.InstrSupport;
@@ -65,7 +66,7 @@ public class ClassAnalyzer extends ClassProbesVisitor {
return new MethodAnalyzer(coverage.getName(), coverage.getSuperName(),
stringPool.get(name), stringPool.get(desc),
- stringPool.get(signature), probes) {
+ stringPool.get(signature), probes, Filters.ALL) {
@Override
public void visitEnd() {
super.visitEnd();
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 e216bb77..0be47158 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
@@ -12,23 +12,17 @@
package org.jacoco.core.internal.analysis;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import org.jacoco.core.analysis.ICounter;
import org.jacoco.core.analysis.IMethodCoverage;
import org.jacoco.core.analysis.ISourceNode;
-import org.jacoco.core.internal.analysis.filter.EnumFilter;
import org.jacoco.core.internal.analysis.filter.IFilter;
import org.jacoco.core.internal.analysis.filter.IFilterOutput;
-import org.jacoco.core.internal.analysis.filter.LombokGeneratedFilter;
-import org.jacoco.core.internal.analysis.filter.PrivateEmptyNoArgConstructorFilter;
-import org.jacoco.core.internal.analysis.filter.StringSwitchJavacFilter;
-import org.jacoco.core.internal.analysis.filter.SynchronizedFilter;
-import org.jacoco.core.internal.analysis.filter.SyntheticFilter;
-import org.jacoco.core.internal.analysis.filter.TryWithResourcesEcjFilter;
-import org.jacoco.core.internal.analysis.filter.TryWithResourcesJavacFilter;
import org.jacoco.core.internal.flow.IFrame;
import org.jacoco.core.internal.flow.Instruction;
import org.jacoco.core.internal.flow.LabelInfo;
@@ -47,18 +41,14 @@ import org.objectweb.asm.tree.TryCatchBlockNode;
public class MethodAnalyzer extends MethodProbesVisitor
implements IFilterOutput {
- private static final IFilter[] FILTERS = new IFilter[] { new EnumFilter(),
- new SyntheticFilter(), new SynchronizedFilter(),
- new TryWithResourcesJavacFilter(), new TryWithResourcesEcjFilter(),
- new PrivateEmptyNoArgConstructorFilter(),
- new StringSwitchJavacFilter(), new LombokGeneratedFilter() };
-
private final String className;
private final String superClassName;
private final boolean[] probes;
+ private final IFilter filter;
+
private final MethodCoverageImpl coverage;
private int currentLine = ISourceNode.UNKNOWN_LINE;
@@ -95,18 +85,20 @@ public class MethodAnalyzer extends MethodProbesVisitor
* method descriptor
* @param signature
* optional parameterized signature
- *
* @param probes
* recorded probe date of the containing class or
* <code>null</code> if the class is not executed at all
+ * @param filter
+ * filter
*/
- public MethodAnalyzer(final String className, final String superClassName,
+ MethodAnalyzer(final String className, final String superClassName,
final String name, final String desc, final String signature,
- final boolean[] probes) {
+ final boolean[] probes, final IFilter filter) {
super();
this.className = className;
this.superClassName = superClassName;
this.probes = probes;
+ this.filter = filter;
this.coverage = new MethodCoverageImpl(name, desc, signature);
}
@@ -126,11 +118,9 @@ public class MethodAnalyzer extends MethodProbesVisitor
@Override
public void accept(final MethodNode methodNode,
final MethodVisitor methodVisitor) {
- this.ignored.clear();
- for (final IFilter filter : FILTERS) {
- filter.filter(className, superClassName, methodNode, this);
- }
+ filter.filter(className, superClassName, methodNode, this);
+ methodVisitor.visitCode();
for (final TryCatchBlockNode n : methodNode.tryCatchBlocks) {
n.accept(methodVisitor);
}
@@ -143,6 +133,22 @@ public class MethodAnalyzer extends MethodProbesVisitor
}
private final Set<AbstractInsnNode> ignored = new HashSet<AbstractInsnNode>();
+
+ /**
+ * Instructions that should be merged form disjoint sets. Coverage
+ * information from instructions of one set will be merged into
+ * representative instruction of set.
+ *
+ * Each such set is represented as a singly linked list: each element except
+ * one references another element from the same set, element without
+ * reference - is a representative of this set.
+ *
+ * This map stores reference (value) for elements of sets (key).
+ */
+ private final Map<AbstractInsnNode, AbstractInsnNode> merged = new HashMap<AbstractInsnNode, AbstractInsnNode>();
+
+ private final Map<AbstractInsnNode, Instruction> nodeToInstruction = new HashMap<AbstractInsnNode, Instruction>();
+
private AbstractInsnNode currentNode;
public void ignore(final AbstractInsnNode fromInclusive,
@@ -154,6 +160,23 @@ public class MethodAnalyzer extends MethodProbesVisitor
ignored.add(toInclusive);
}
+ private AbstractInsnNode findRepresentative(AbstractInsnNode i) {
+ AbstractInsnNode r = merged.get(i);
+ while (r != null) {
+ i = r;
+ r = merged.get(i);
+ }
+ return i;
+ }
+
+ public void merge(AbstractInsnNode i1, AbstractInsnNode i2) {
+ i1 = findRepresentative(i1);
+ i2 = findRepresentative(i2);
+ if (i1 != i2) {
+ merged.put(i2, i1);
+ }
+ }
+
@Override
public void visitLabel(final Label label) {
currentLabel.add(label);
@@ -175,6 +198,7 @@ public class MethodAnalyzer extends MethodProbesVisitor
private void visitInsn() {
final Instruction insn = new Instruction(currentNode, currentLine);
+ nodeToInstruction.put(currentNode, insn);
instructions.add(insn);
if (lastInsn != null) {
insn.setPredecessor(lastInsn, 0);
@@ -342,6 +366,15 @@ 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();
+ final AbstractInsnNode r = findRepresentative(m);
+ if (r != m) {
+ ignored.add(m);
+ nodeToInstruction.get(r).merge(i);
+ }
+ }
// Report result:
coverage.ensureCapacity(firstLine, lastLine);
for (final Instruction i : instructions) {
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
new file mode 100644
index 00000000..e2c26d92
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/Filters.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2017 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 org.objectweb.asm.tree.MethodNode;
+
+/**
+ * Filter that combines other filters.
+ */
+public final class Filters implements IFilter {
+
+ /**
+ * Filter that does nothing.
+ */
+ public static final IFilter NONE = new Filters();
+
+ /**
+ * Filter that combines all other filters.
+ */
+ public static final IFilter ALL = new Filters(new EnumFilter(),
+ new SyntheticFilter(), new SynchronizedFilter(),
+ new TryWithResourcesJavacFilter(), new TryWithResourcesEcjFilter(),
+ new PrivateEmptyNoArgConstructorFilter(),
+ new StringSwitchJavacFilter(), new LombokGeneratedFilter());
+
+ private final IFilter[] filters;
+
+ private Filters(IFilter... filters) {
+ this.filters = filters;
+ }
+
+ public void filter(final String className, final String superClassName,
+ final MethodNode methodNode, final IFilterOutput output) {
+ for (final IFilter filter : filters) {
+ filter.filter(className, superClassName, methodNode, output);
+ }
+ }
+
+}
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 c24dc03d..cf25a0a2 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
@@ -30,4 +30,15 @@ public interface IFilterOutput {
*/
void ignore(AbstractInsnNode fromInclusive, AbstractInsnNode toInclusive);
+ /**
+ * Marks two instructions that should be merged during computation of
+ * coverage.
+ *
+ * @param i1
+ * first instruction
+ * @param i2
+ * second instruction
+ */
+ void merge(AbstractInsnNode i1, AbstractInsnNode i2);
+
}
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/LombokGeneratedFilter.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/LombokGeneratedFilter.java
index 571e4641..98575e80 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/LombokGeneratedFilter.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/LombokGeneratedFilter.java
@@ -19,7 +19,7 @@ import org.objectweb.asm.tree.MethodNode;
/**
* Filters methods annotated with <code>@lombok.Generated</code>.
*/
-public class LombokGeneratedFilter implements IFilter {
+public final class LombokGeneratedFilter implements IFilter {
public void filter(final String className, final String superClassName,
final MethodNode methodNode, final IFilterOutput output) {
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/flow/Instruction.java b/org.jacoco.core/src/org/jacoco/core/internal/flow/Instruction.java
index 4f550407..bee274db 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/flow/Instruction.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/flow/Instruction.java
@@ -130,6 +130,17 @@ public class Instruction {
return coveredBranches.cardinality();
}
+ /**
+ * Merges information about covered branches of given instruction into this
+ * instruction.
+ *
+ * @param instruction
+ * instruction from which to merge
+ */
+ public void merge(Instruction instruction) {
+ this.coveredBranches.or(instruction.coveredBranches);
+ }
+
@Override
public String toString() {
return coveredBranches.toString();