diff options
Diffstat (limited to 'org.jacoco.core/src/org/jacoco/core/internal/analysis')
4 files changed, 200 insertions, 2 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 b27c54bc..6736acae 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,36 @@ package org.jacoco.core.internal.analysis; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +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.IFilter; +import org.jacoco.core.internal.analysis.filter.IFilterOutput; +import org.jacoco.core.internal.analysis.filter.SynchronizedFilter; import org.jacoco.core.internal.flow.IFrame; import org.jacoco.core.internal.flow.Instruction; import org.jacoco.core.internal.flow.LabelInfo; import org.jacoco.core.internal.flow.MethodProbesVisitor; import org.objectweb.asm.Handle; import org.objectweb.asm.Label; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.MethodNode; +import org.objectweb.asm.tree.TryCatchBlockNode; /** * A {@link MethodProbesVisitor} that analyzes which statements and branches of * a method have been executed based on given probe data. */ -public class MethodAnalyzer extends MethodProbesVisitor { +public class MethodAnalyzer extends MethodProbesVisitor + implements IFilterOutput { + + private static final IFilter[] FILTERS = new IFilter[] { + new SynchronizedFilter() }; private final boolean[] probes; @@ -86,6 +99,40 @@ public class MethodAnalyzer extends MethodProbesVisitor { return coverage; } + /** + * {@link MethodNode#accept(MethodVisitor)} + */ + @Override + public void accept(final MethodNode methodNode, + final MethodVisitor methodVisitor) { + this.ignored.clear(); + for (final IFilter filter : FILTERS) { + filter.filter(methodNode, this); + } + + for (final TryCatchBlockNode n : methodNode.tryCatchBlocks) { + n.accept(methodVisitor); + } + currentNode = methodNode.instructions.getFirst(); + while (currentNode != null) { + currentNode.accept(methodVisitor); + currentNode = currentNode.getNext(); + } + methodVisitor.visitEnd(); + } + + private final Set<AbstractInsnNode> ignored = new HashSet<AbstractInsnNode>(); + private AbstractInsnNode currentNode; + + public void ignore(final AbstractInsnNode fromInclusive, + final AbstractInsnNode toInclusive) { + for (AbstractInsnNode i = fromInclusive; i != toInclusive; i = i + .getNext()) { + ignored.add(i); + } + ignored.add(toInclusive); + } + @Override public void visitLabel(final Label label) { currentLabel.add(label); @@ -106,7 +153,7 @@ public class MethodAnalyzer extends MethodProbesVisitor { } private void visitInsn() { - final Instruction insn = new Instruction(currentLine); + final Instruction insn = new Instruction(currentNode, currentLine); instructions.add(insn); if (lastInsn != null) { insn.setPredecessor(lastInsn); @@ -272,6 +319,10 @@ public class MethodAnalyzer extends MethodProbesVisitor { // Report result: coverage.ensureCapacity(firstLine, lastLine); for (final Instruction i : instructions) { + if (ignored.contains(i.getNode())) { + continue; + } + final int total = i.getBranches(); final int covered = i.getCoveredBranches(); final ICounter instrCounter = covered == 0 ? CounterImpl.COUNTER_1_0 diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/IFilter.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/IFilter.java new file mode 100644 index 00000000..ab2daa8e --- /dev/null +++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/IFilter.java @@ -0,0 +1,24 @@ +/******************************************************************************* + * 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; + +/** + * Interface for filter implementations. Instances of filters are reused and so + * must be stateless. + */ +public interface IFilter { + + void filter(MethodNode methodNode, IFilterOutput 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 new file mode 100644 index 00000000..c24dc03d --- /dev/null +++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/IFilterOutput.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * 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.AbstractInsnNode; + +/** + * Interface used by filters to mark filtered items. + */ +public interface IFilterOutput { + + /** + * Marks sequence of instructions that should be ignored during computation + * of coverage. + * + * @param fromInclusive + * first instruction that should be ignored, inclusive + * @param toInclusive + * last instruction coming after <code>fromInclusive</code> that + * should be ignored, inclusive + */ + void ignore(AbstractInsnNode fromInclusive, AbstractInsnNode toInclusive); + +} diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/SynchronizedFilter.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/SynchronizedFilter.java new file mode 100644 index 00000000..65e763d5 --- /dev/null +++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/SynchronizedFilter.java @@ -0,0 +1,90 @@ +/******************************************************************************* + * 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.Opcodes; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.MethodNode; +import org.objectweb.asm.tree.TryCatchBlockNode; + +/** + * Filters code that is generated for synchronized statement. + */ +public final class SynchronizedFilter implements IFilter { + + public void filter(MethodNode methodNode, IFilterOutput output) { + for (TryCatchBlockNode tryCatch : methodNode.tryCatchBlocks) { + if (tryCatch.type != null) { + continue; + } + if (tryCatch.start == tryCatch.handler) { + continue; + } + final AbstractInsnNode to = new Matcher(tryCatch.handler).match(); + if (to == null) { + continue; + } + output.ignore(tryCatch.handler, to); + } + } + + private static class Matcher { + private final AbstractInsnNode start; + private AbstractInsnNode cursor; + + private Matcher(final AbstractInsnNode start) { + this.start = start; + } + + private AbstractInsnNode match() { + if (nextIsEcj() || nextIsJavac()) { + return cursor; + } + return null; + } + + private boolean nextIsJavac() { + cursor = start; + return nextIs(Opcodes.ASTORE) && nextIs(Opcodes.ALOAD) + && nextIs(Opcodes.MONITOREXIT) && nextIs(Opcodes.ALOAD) + && nextIs(Opcodes.ATHROW); + } + + private boolean nextIsEcj() { + cursor = start; + return nextIs(Opcodes.ALOAD) && nextIs(Opcodes.MONITOREXIT) + && nextIs(Opcodes.ATHROW); + } + + /** + * Moves {@link #cursor} to next instruction and returns + * <code>true</code> if it has given opcode. + */ + private boolean nextIs(int opcode) { + next(); + return cursor != null && cursor.getOpcode() == opcode; + } + + /** + * Moves {@link #cursor} to next instruction. + */ + private void next() { + do { + cursor = cursor.getNext(); + } while (cursor != null + && (cursor.getType() == AbstractInsnNode.FRAME + || cursor.getType() == AbstractInsnNode.LABEL + || cursor.getType() == AbstractInsnNode.LINE)); + } + } + +} |