| /******************************************************************************* |
| * 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: |
| * Marc R. Hoffmann - initial API and implementation |
| * |
| *******************************************************************************/ |
| package org.jacoco.core.internal.flow; |
| |
| import java.util.HashMap; |
| import java.util.Map; |
| |
| import org.jacoco.core.internal.instr.InstrSupport; |
| import org.objectweb.asm.Label; |
| import org.objectweb.asm.MethodVisitor; |
| import org.objectweb.asm.Opcodes; |
| import org.objectweb.asm.commons.AnalyzerAdapter; |
| |
| /** |
| * Adapter that creates additional visitor events for probes to be inserted into |
| * a method. |
| */ |
| public final class MethodProbesAdapter extends MethodVisitor { |
| |
| private final MethodProbesVisitor probesVisitor; |
| |
| private final IProbeIdGenerator idGenerator; |
| |
| private AnalyzerAdapter analyzer; |
| |
| private final Map<Label, Label> tryCatchProbeLabels; |
| |
| /** |
| * Create a new adapter instance. |
| * |
| * @param probesVisitor |
| * visitor to delegate to |
| * @param idGenerator |
| * generator for unique probe ids |
| */ |
| public MethodProbesAdapter(final MethodProbesVisitor probesVisitor, |
| final IProbeIdGenerator idGenerator) { |
| super(InstrSupport.ASM_API_VERSION, probesVisitor); |
| this.probesVisitor = probesVisitor; |
| this.idGenerator = idGenerator; |
| this.tryCatchProbeLabels = new HashMap<Label, Label>(); |
| } |
| |
| /** |
| * If an analyzer is set {@link IFrame} handles are calculated and emitted |
| * to the probes methods. |
| * |
| * @param analyzer |
| * optional analyzer to set |
| */ |
| public void setAnalyzer(final AnalyzerAdapter analyzer) { |
| this.analyzer = analyzer; |
| } |
| |
| @Override |
| public void visitTryCatchBlock(final Label start, final Label end, |
| final Label handler, final String type) { |
| probesVisitor.visitTryCatchBlock(getTryCatchLabel(start), getTryCatchLabel(end), |
| handler, type); |
| } |
| |
| private Label getTryCatchLabel(Label label) { |
| if (tryCatchProbeLabels.containsKey(label)) { |
| label = tryCatchProbeLabels.get(label); |
| } else if (LabelInfo.needsProbe(label)) { |
| // If a probe will be inserted before the label, we'll need to use a |
| // different label to define the range of the try-catch block. |
| final Label probeLabel = new Label(); |
| LabelInfo.setSuccessor(probeLabel); |
| tryCatchProbeLabels.put(label, probeLabel); |
| label = probeLabel; |
| } |
| return label; |
| } |
| |
| @Override |
| public void visitLabel(final Label label) { |
| if (LabelInfo.needsProbe(label)) { |
| if (tryCatchProbeLabels.containsKey(label)) { |
| probesVisitor.visitLabel(tryCatchProbeLabels.get(label)); |
| } |
| probesVisitor.visitProbe(idGenerator.nextId()); |
| } |
| probesVisitor.visitLabel(label); |
| } |
| |
| @Override |
| public void visitInsn(final int opcode) { |
| switch (opcode) { |
| case Opcodes.IRETURN: |
| case Opcodes.LRETURN: |
| case Opcodes.FRETURN: |
| case Opcodes.DRETURN: |
| case Opcodes.ARETURN: |
| case Opcodes.RETURN: |
| case Opcodes.ATHROW: |
| probesVisitor.visitInsnWithProbe(opcode, idGenerator.nextId()); |
| break; |
| default: |
| probesVisitor.visitInsn(opcode); |
| break; |
| } |
| } |
| |
| @Override |
| public void visitJumpInsn(final int opcode, final Label label) { |
| if (LabelInfo.isMultiTarget(label)) { |
| probesVisitor.visitJumpInsnWithProbe(opcode, label, |
| idGenerator.nextId(), frame(jumpPopCount(opcode))); |
| } else { |
| probesVisitor.visitJumpInsn(opcode, label); |
| } |
| } |
| |
| private int jumpPopCount(final int opcode) { |
| switch (opcode) { |
| case Opcodes.GOTO: |
| return 0; |
| case Opcodes.IFEQ: |
| case Opcodes.IFNE: |
| case Opcodes.IFLT: |
| case Opcodes.IFGE: |
| case Opcodes.IFGT: |
| case Opcodes.IFLE: |
| case Opcodes.IFNULL: |
| case Opcodes.IFNONNULL: |
| return 1; |
| default: // IF_CMPxx and IF_ACMPxx |
| return 2; |
| } |
| } |
| |
| @Override |
| public void visitLookupSwitchInsn(final Label dflt, final int[] keys, |
| final Label[] labels) { |
| if (markLabels(dflt, labels)) { |
| probesVisitor.visitLookupSwitchInsnWithProbes(dflt, keys, labels, |
| frame(1)); |
| } else { |
| probesVisitor.visitLookupSwitchInsn(dflt, keys, labels); |
| } |
| } |
| |
| @Override |
| public void visitTableSwitchInsn(final int min, final int max, |
| final Label dflt, final Label... labels) { |
| if (markLabels(dflt, labels)) { |
| probesVisitor.visitTableSwitchInsnWithProbes(min, max, dflt, |
| labels, frame(1)); |
| } else { |
| probesVisitor.visitTableSwitchInsn(min, max, dflt, labels); |
| } |
| } |
| |
| private boolean markLabels(final Label dflt, final Label[] labels) { |
| boolean probe = false; |
| LabelInfo.resetDone(labels); |
| if (LabelInfo.isMultiTarget(dflt)) { |
| LabelInfo.setProbeId(dflt, idGenerator.nextId()); |
| probe = true; |
| } |
| LabelInfo.setDone(dflt); |
| for (final Label l : labels) { |
| if (LabelInfo.isMultiTarget(l) && !LabelInfo.isDone(l)) { |
| LabelInfo.setProbeId(l, idGenerator.nextId()); |
| probe = true; |
| } |
| LabelInfo.setDone(l); |
| } |
| return probe; |
| } |
| |
| private IFrame frame(final int popCount) { |
| return FrameSnapshot.create(analyzer, popCount); |
| } |
| |
| } |