diff options
author | Evgeny Mandrikov <Godin@users.noreply.github.com> | 2017-09-29 06:06:11 +0200 |
---|---|---|
committer | Marc R. Hoffmann <hoffmann@mountainminds.com> | 2017-09-29 06:06:11 +0200 |
commit | 1962cf59bc403a27f68c55b39bb833b00fa386a0 (patch) | |
tree | 2bca7259d0e9cfe50630f430b8a56a9bf3adbbc8 /org.jacoco.core/src/org/jacoco | |
parent | aca2da1d7479d6b536e027e8fa7db74428d781e6 (diff) | |
download | jacoco-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')
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(); |