diff options
author | Evgeny Mandrikov <Godin@users.noreply.github.com> | 2017-03-28 19:20:48 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-03-28 19:20:48 +0200 |
commit | eaef191fe12fc5abc81435016dbbd69cb55d1f68 (patch) | |
tree | f7ea74f56ff9a388e22cb44e5626dd3bcb83f50e | |
parent | 38a26f418483fd2053b19bf6b3adb7318c11be3f (diff) | |
download | jacoco-eaef191fe12fc5abc81435016dbbd69cb55d1f68.tar.gz |
Add internal API for filters and filter for synchronized statement (#501)
14 files changed, 556 insertions, 12 deletions
diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/SynchronizedFilterTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/SynchronizedFilterTest.java new file mode 100644 index 00000000..b315dda5 --- /dev/null +++ b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/SynchronizedFilterTest.java @@ -0,0 +1,166 @@ +/******************************************************************************* + * 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 static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import org.jacoco.core.internal.instr.InstrSupport; +import org.junit.Test; +import org.objectweb.asm.Label; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.LabelNode; +import org.objectweb.asm.tree.MethodNode; + +public class SynchronizedFilterTest implements IFilterOutput { + + private final SynchronizedFilter filter = new SynchronizedFilter(); + + private final MethodNode m = new MethodNode(InstrSupport.ASM_API_VERSION, 0, + "name", "()V", null, null); + + private AbstractInsnNode fromInclusive; + private AbstractInsnNode toInclusive; + + @Test + public void javac() { + final Label start = new Label(); + final Label end = new Label(); + final Label handler = new Label(); + final Label handlerEnd = new Label(); + m.visitTryCatchBlock(start, end, handler, null); + m.visitTryCatchBlock(handler, handlerEnd, handler, null); + + m.visitVarInsn(Opcodes.ALOAD, 0); + m.visitFieldInsn(Opcodes.GETFIELD, "Fun", "lock", "Ljava/lang/Object;"); + m.visitInsn(Opcodes.DUP); + m.visitVarInsn(Opcodes.ASTORE, 1); + m.visitInsn(Opcodes.MONITORENTER); + m.visitLabel(start); + m.visitInsn(Opcodes.NOP); + m.visitVarInsn(Opcodes.ALOAD, 1); + m.visitInsn(Opcodes.MONITOREXIT); + m.visitLabel(end); + final Label exit = new Label(); + m.visitJumpInsn(Opcodes.GOTO, exit); + m.visitLabel(handler); + m.visitVarInsn(Opcodes.ASTORE, 2); + m.visitVarInsn(Opcodes.ALOAD, 1); + m.visitInsn(Opcodes.MONITOREXIT); + m.visitLabel(handlerEnd); + m.visitVarInsn(Opcodes.ALOAD, 2); + m.visitInsn(Opcodes.ATHROW); + m.visitLabel(exit); + m.visitInsn(Opcodes.RETURN); + + filter.filter(m, this); + assertEquals(handler.info, fromInclusive); + assertEquals(((LabelNode) exit.info).getPrevious(), toInclusive); + } + + /** + * <pre> + * try { + * ... + * } catch (Exception e) { + * ... + * } finally { + * ... + * } + * </pre> + */ + @Test + public void javacTryCatchFinally() { + final Label start = new Label(); + final Label end = new Label(); + final Label catchHandler = new Label(); + final Label finallyHandler = new Label(); + final Label catchHandlerEnd = new Label(); + m.visitTryCatchBlock(start, end, catchHandler, "java/lang/Exception"); + m.visitTryCatchBlock(start, end, finallyHandler, null); + m.visitTryCatchBlock(catchHandler, catchHandlerEnd, finallyHandler, + null); + + m.visitLabel(start); + // body + m.visitInsn(Opcodes.NOP); + m.visitLabel(end); + // finally + m.visitInsn(Opcodes.NOP); + final Label exit = new Label(); + m.visitJumpInsn(Opcodes.GOTO, exit); + m.visitLabel(catchHandler); + m.visitVarInsn(Opcodes.ASTORE, 1); + // catch + m.visitInsn(Opcodes.NOP); + m.visitLabel(catchHandlerEnd); + // finally + m.visitInsn(Opcodes.NOP); + m.visitJumpInsn(Opcodes.GOTO, exit); + m.visitLabel(finallyHandler); + m.visitVarInsn(Opcodes.ASTORE, 2); + // finally + m.visitInsn(Opcodes.NOP); + m.visitVarInsn(Opcodes.ALOAD, 2); + m.visitInsn(Opcodes.ATHROW); + m.visitLabel(exit); + m.visitInsn(Opcodes.RETURN); + + filter.filter(m, this); + assertNull(fromInclusive); + } + + @Test + public void ecj() { + final Label start = new Label(); + final Label end = new Label(); + final Label handler = new Label(); + final Label handlerEnd = new Label(); + m.visitTryCatchBlock(start, end, handler, null); + m.visitTryCatchBlock(handler, handlerEnd, handler, null); + + m.visitVarInsn(Opcodes.ALOAD, 0); + m.visitFieldInsn(Opcodes.GETFIELD, "Target", "lock", + "Ljava/lang/Object;"); + m.visitInsn(Opcodes.DUP); + m.visitVarInsn(Opcodes.ASTORE, 1); + m.visitInsn(Opcodes.MONITORENTER); + m.visitLabel(start); + m.visitVarInsn(Opcodes.ALOAD, 0); + m.visitInsn(Opcodes.NOP); + m.visitVarInsn(Opcodes.ALOAD, 1); + m.visitInsn(Opcodes.MONITOREXIT); + m.visitLabel(end); + final Label exit = new Label(); + m.visitJumpInsn(Opcodes.GOTO, exit); + m.visitLabel(handler); + m.visitVarInsn(Opcodes.ALOAD, 1); + m.visitInsn(Opcodes.MONITOREXIT); + m.visitLabel(handlerEnd); + m.visitInsn(Opcodes.ATHROW); + m.visitLabel(exit); + m.visitInsn(Opcodes.RETURN); + + filter.filter(m, this); + assertEquals(handler.info, fromInclusive); + assertEquals(((LabelNode) exit.info).getPrevious(), toInclusive); + } + + public void ignore(AbstractInsnNode fromInclusive, + AbstractInsnNode toInclusive) { + assertNull(this.fromInclusive); + this.fromInclusive = fromInclusive; + this.toInclusive = toInclusive; + } + +} diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/flow/InstructionTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/flow/InstructionTest.java index 6ce99ed7..1dc5fc2c 100644 --- a/org.jacoco.core.test/src/org/jacoco/core/internal/flow/InstructionTest.java +++ b/org.jacoco.core.test/src/org/jacoco/core/internal/flow/InstructionTest.java @@ -12,9 +12,12 @@ package org.jacoco.core.internal.flow; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; import org.junit.Before; import org.junit.Test; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.InsnNode; /** * Unit tests for {@link Instruction}. @@ -25,11 +28,14 @@ public class InstructionTest { @Before public void setup() { - instruction = new Instruction(123); + instruction = new Instruction(new InsnNode(Opcodes.NOP), 123); } @Test public void testInit() { + final InsnNode node = new InsnNode(Opcodes.NOP); + instruction = new Instruction(node, 123); + assertSame(node, instruction.getNode()); assertEquals(123, instruction.getLine()); assertEquals(0, instruction.getBranches()); assertEquals(0, instruction.getCoveredBranches()); @@ -48,14 +54,16 @@ public class InstructionTest { @Test public void testSetPredecessor() { - final Instruction predecessor = new Instruction(122); + final Instruction predecessor = new Instruction( + new InsnNode(Opcodes.NOP), 122); instruction.setPredecessor(predecessor); assertEquals(1, predecessor.getBranches()); } @Test public void testSetCovered() { - final Instruction predecessor = new Instruction(122); + final Instruction predecessor = new Instruction( + new InsnNode(Opcodes.NOP), 122); instruction.setPredecessor(predecessor); instruction.setCovered(); assertEquals(1, instruction.getCoveredBranches()); @@ -68,10 +76,11 @@ public class InstructionTest { @Test public void testSetCoveredOnLongSequence() { - final Instruction first = new Instruction(0); + final Instruction first = new Instruction(new InsnNode(Opcodes.NOP), 0); Instruction next = first; for (int i = 0; i < 0x10000; i++) { - final Instruction insn = new Instruction(i); + final Instruction insn = new Instruction(new InsnNode(Opcodes.NOP), + i); insn.setPredecessor(next); next = insn; } diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/flow/LabelInfoTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/flow/LabelInfoTest.java index b412080a..b2fd0b5d 100644 --- a/org.jacoco.core.test/src/org/jacoco/core/internal/flow/LabelInfoTest.java +++ b/org.jacoco.core.test/src/org/jacoco/core/internal/flow/LabelInfoTest.java @@ -20,6 +20,8 @@ import static org.junit.Assert.assertTrue; import org.junit.Before; import org.junit.Test; import org.objectweb.asm.Label; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.InsnNode; /** * Unit tests for {@link LabelInfoTest}. @@ -159,7 +161,8 @@ public class LabelInfoTest { @Test public void testSetInstruction() { - final Instruction instruction = new Instruction(123); + final Instruction instruction = new Instruction( + new InsnNode(Opcodes.NOP), 123); LabelInfo.setInstruction(label, instruction); assertSame(instruction, LabelInfo.getInstruction(label)); } diff --git a/org.jacoco.core.test/src/org/jacoco/core/test/filter/SynchronizedTest.java b/org.jacoco.core.test/src/org/jacoco/core/test/filter/SynchronizedTest.java new file mode 100644 index 00000000..1cfce301 --- /dev/null +++ b/org.jacoco.core.test/src/org/jacoco/core/test/filter/SynchronizedTest.java @@ -0,0 +1,74 @@ +/******************************************************************************* + * 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.test.filter; + +import org.jacoco.core.analysis.ICounter; +import org.jacoco.core.test.filter.targets.Synchronized; +import org.jacoco.core.test.validation.ValidationTestBase; +import org.junit.Test; + +/** + * Test of filtering of a bytecode that is generated for a synchronized + * statement. + */ +public class SynchronizedTest extends ValidationTestBase { + + public SynchronizedTest() { + super(Synchronized.class); + } + + /** + * {@link Synchronized#normal()} + */ + @Test + public void normal() { + assertLine("before", ICounter.FULLY_COVERED); + // when compiled with ECJ next line covered partly without filter: + assertLine("monitorEnter", ICounter.FULLY_COVERED); + assertLine("body", ICounter.FULLY_COVERED); + if (isJDKCompiler) { + // without filter next line covered partly: + assertLine("monitorExit", ICounter.FULLY_COVERED); + } else { + assertLine("monitorExit", ICounter.EMPTY); + } + assertLine("after", ICounter.FULLY_COVERED); + } + + /** + * {@link Synchronized#explicitException()} + */ + @Test + public void explicitException() { + assertLine("explicitException.monitorEnter", ICounter.FULLY_COVERED); + assertLine("explicitException.exception", ICounter.FULLY_COVERED); + // when compiled with javac next line covered fully without filter: + assertLine("explicitException.monitorExit", ICounter.EMPTY); + } + + /** + * {@link Synchronized#implicitException()} + */ + @Test + public void implicitException() { + assertLine("implicitException.monitorEnter", isJDKCompiler + ? ICounter.FULLY_COVERED : ICounter.PARTLY_COVERED); + assertLine("implicitException.exception", ICounter.NOT_COVERED); + if (isJDKCompiler) { + // without filter next line covered partly: + assertLine("implicitException.monitorExit", ICounter.NOT_COVERED); + } else { + assertLine("implicitException.monitorExit", ICounter.EMPTY); + } + } + +} diff --git a/org.jacoco.core.test/src/org/jacoco/core/test/filter/targets/Synchronized.java b/org.jacoco.core.test/src/org/jacoco/core/test/filter/targets/Synchronized.java new file mode 100644 index 00000000..94030978 --- /dev/null +++ b/org.jacoco.core.test/src/org/jacoco/core/test/filter/targets/Synchronized.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * 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.test.filter.targets; + +import static org.jacoco.core.test.validation.targets.Stubs.ex; +import static org.jacoco.core.test.validation.targets.Stubs.nop; + +import org.jacoco.core.test.validation.targets.Stubs.StubException; + +/** + * This test target is a synchronized statement. + */ +public class Synchronized { + + private static final Object lock = new Object(); + + private static void normal() { + nop(); // $line-before$ + synchronized (lock) { // $line-monitorEnter$ + nop(); // $line-body$ + } // $line-monitorExit$ + nop(); // $line-after$ + } + + private static void explicitException() { + synchronized (lock) { // $line-explicitException.monitorEnter$ + throw new StubException(); // $line-explicitException.exception$ + } // $line-explicitException.monitorExit$ + } + + private static void implicitException() { + synchronized (lock) { // $line-implicitException.monitorEnter$ + ex(); // $line-implicitException.exception$ + } // $line-implicitException.monitorExit$ + } + + public static void main(String[] args) { + normal(); + + try { + explicitException(); + } catch (StubException e) { + } + + try { + implicitException(); + } catch (StubException e) { + } + } + +} diff --git a/org.jacoco.core.test/src/org/jacoco/core/test/validation/ValidationTestBase.java b/org.jacoco.core.test/src/org/jacoco/core/test/validation/ValidationTestBase.java index aa6b35bd..46a19a5a 100644 --- a/org.jacoco.core.test/src/org/jacoco/core/test/validation/ValidationTestBase.java +++ b/org.jacoco.core.test/src/org/jacoco/core/test/validation/ValidationTestBase.java @@ -37,7 +37,7 @@ import org.junit.Before; */ public abstract class ValidationTestBase { - static final boolean isJDKCompiler = Compiler.DETECT.isJDK(); + protected static final boolean isJDKCompiler = Compiler.DETECT.isJDK(); private static final String[] STATUS_NAME = new String[4]; 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)); + } + } + +} diff --git a/org.jacoco.core/src/org/jacoco/core/internal/flow/ClassProbesAdapter.java b/org.jacoco.core/src/org/jacoco/core/internal/flow/ClassProbesAdapter.java index 860d8fbe..c30b768a 100644 --- a/org.jacoco.core/src/org/jacoco/core/internal/flow/ClassProbesAdapter.java +++ b/org.jacoco.core/src/org/jacoco/core/internal/flow/ClassProbesAdapter.java @@ -84,9 +84,9 @@ public class ClassProbesAdapter extends ClassVisitor implements ClassProbesAdapter.this.name, access, name, desc, probesAdapter); probesAdapter.setAnalyzer(analyzer); - this.accept(analyzer); + methodProbes.accept(this, analyzer); } else { - this.accept(probesAdapter); + methodProbes.accept(this, probesAdapter); } } }; 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 e49c5a0f..e41ca468 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 @@ -11,12 +11,16 @@ *******************************************************************************/ package org.jacoco.core.internal.flow; +import org.objectweb.asm.tree.AbstractInsnNode; + /** * Representation of a byte code instruction for analysis. Internally used for * analysis. */ public class Instruction { + private final AbstractInsnNode node; + private final int line; private int branches; @@ -28,16 +32,26 @@ public class Instruction { /** * New instruction at the given line. * + * @param node + * corresponding node * @param line * source line this instruction belongs to */ - public Instruction(final int line) { + public Instruction(final AbstractInsnNode node, final int line) { + this.node = node; this.line = line; this.branches = 0; this.coveredBranches = 0; } /** + * @return corresponding node + */ + public AbstractInsnNode getNode() { + return node; + } + + /** * Adds an branch to this instruction. */ public void addBranch() { diff --git a/org.jacoco.core/src/org/jacoco/core/internal/flow/MethodProbesVisitor.java b/org.jacoco.core/src/org/jacoco/core/internal/flow/MethodProbesVisitor.java index b212b023..a5094063 100644 --- a/org.jacoco.core/src/org/jacoco/core/internal/flow/MethodProbesVisitor.java +++ b/org.jacoco.core/src/org/jacoco/core/internal/flow/MethodProbesVisitor.java @@ -14,6 +14,7 @@ package org.jacoco.core.internal.flow; import org.jacoco.core.internal.instr.InstrSupport; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.tree.MethodNode; /** * A {@link MethodVisitor} with additional methods to get probe insertion @@ -147,4 +148,20 @@ public abstract class MethodProbesVisitor extends MethodVisitor { final int[] keys, final Label[] labels, final IFrame frame) { } + /** + * This method can be overwritten to hook into the process of emitting the + * instructions of this method as <code>visitX()</code> events. + * + * @param methodNode + * the content to emit + * @param methodVisitor + * A visitor to emit the content to. Note that this is not + * necessarily this visitor instance but some wrapper which + * calculates the probes. + */ + public void accept(final MethodNode methodNode, + final MethodVisitor methodVisitor) { + methodNode.accept(methodVisitor); + } + } diff --git a/org.jacoco.doc/docroot/doc/changes.html b/org.jacoco.doc/docroot/doc/changes.html index 80d7fb71..c73b155d 100644 --- a/org.jacoco.doc/docroot/doc/changes.html +++ b/org.jacoco.doc/docroot/doc/changes.html @@ -25,6 +25,9 @@ <li>Exclude from a report enum methods <code>valueOf</code> and <code>values</code> that are created by compiler (GitHub <a href="https://github.com/jacoco/jacoco/issues/491">#491</a>).</li> + <li>Exclude from a report a part of bytecode that compiler generates for a + synchronized statement + (GitHub <a href="https://github.com/jacoco/jacoco/issues/501">#501</a>).</li> <li>Maven aggregated reports will now also include modules of runtime dependencies (GitHub <a href="https://github.com/jacoco/jacoco/issues/498">#498</a>).</li> </ul> |