1 /******************************************************************************* 2 * Copyright (c) 2009, 2021 Mountainminds GmbH & Co. KG and Contributors 3 * This program and the accompanying materials are made available under 4 * the terms of the Eclipse Public License 2.0 which is available at 5 * http://www.eclipse.org/legal/epl-2.0 6 * 7 * SPDX-License-Identifier: EPL-2.0 8 * 9 * Contributors: 10 * Marc R. Hoffmann - initial API and implementation 11 * 12 *******************************************************************************/ 13 package org.jacoco.core.internal.flow; 14 15 import java.util.HashMap; 16 import java.util.Map; 17 18 import org.jacoco.core.internal.instr.InstrSupport; 19 import org.objectweb.asm.Label; 20 import org.objectweb.asm.MethodVisitor; 21 import org.objectweb.asm.Opcodes; 22 import org.objectweb.asm.commons.AnalyzerAdapter; 23 24 /** 25 * Adapter that creates additional visitor events for probes to be inserted into 26 * a method. 27 */ 28 public final class MethodProbesAdapter extends MethodVisitor { 29 30 private final MethodProbesVisitor probesVisitor; 31 32 private final IProbeIdGenerator idGenerator; 33 34 private AnalyzerAdapter analyzer; 35 36 private final Map<Label, Label> tryCatchProbeLabels; 37 38 /** 39 * Create a new adapter instance. 40 * 41 * @param probesVisitor 42 * visitor to delegate to 43 * @param idGenerator 44 * generator for unique probe ids 45 */ MethodProbesAdapter(final MethodProbesVisitor probesVisitor, final IProbeIdGenerator idGenerator)46 public MethodProbesAdapter(final MethodProbesVisitor probesVisitor, 47 final IProbeIdGenerator idGenerator) { 48 super(InstrSupport.ASM_API_VERSION, probesVisitor); 49 this.probesVisitor = probesVisitor; 50 this.idGenerator = idGenerator; 51 this.tryCatchProbeLabels = new HashMap<Label, Label>(); 52 } 53 54 /** 55 * If an analyzer is set {@link IFrame} handles are calculated and emitted 56 * to the probes methods. 57 * 58 * @param analyzer 59 * optional analyzer to set 60 */ setAnalyzer(final AnalyzerAdapter analyzer)61 public void setAnalyzer(final AnalyzerAdapter analyzer) { 62 this.analyzer = analyzer; 63 } 64 65 @Override visitTryCatchBlock(final Label start, final Label end, final Label handler, final String type)66 public void visitTryCatchBlock(final Label start, final Label end, 67 final Label handler, final String type) { 68 probesVisitor.visitTryCatchBlock(getTryCatchLabel(start), 69 getTryCatchLabel(end), handler, type); 70 } 71 getTryCatchLabel(Label label)72 private Label getTryCatchLabel(Label label) { 73 if (tryCatchProbeLabels.containsKey(label)) { 74 label = tryCatchProbeLabels.get(label); 75 } else if (LabelInfo.needsProbe(label)) { 76 // If a probe will be inserted before the label, we'll need to use a 77 // different label to define the range of the try-catch block. 78 final Label probeLabel = new Label(); 79 LabelInfo.setSuccessor(probeLabel); 80 tryCatchProbeLabels.put(label, probeLabel); 81 label = probeLabel; 82 } 83 return label; 84 } 85 86 @Override visitLabel(final Label label)87 public void visitLabel(final Label label) { 88 if (LabelInfo.needsProbe(label)) { 89 if (tryCatchProbeLabels.containsKey(label)) { 90 probesVisitor.visitLabel(tryCatchProbeLabels.get(label)); 91 } 92 probesVisitor.visitProbe(idGenerator.nextId()); 93 } 94 probesVisitor.visitLabel(label); 95 } 96 97 @Override visitInsn(final int opcode)98 public void visitInsn(final int opcode) { 99 switch (opcode) { 100 case Opcodes.IRETURN: 101 case Opcodes.LRETURN: 102 case Opcodes.FRETURN: 103 case Opcodes.DRETURN: 104 case Opcodes.ARETURN: 105 case Opcodes.RETURN: 106 case Opcodes.ATHROW: 107 probesVisitor.visitInsnWithProbe(opcode, idGenerator.nextId()); 108 break; 109 default: 110 probesVisitor.visitInsn(opcode); 111 break; 112 } 113 } 114 115 @Override visitJumpInsn(final int opcode, final Label label)116 public void visitJumpInsn(final int opcode, final Label label) { 117 if (LabelInfo.isMultiTarget(label)) { 118 probesVisitor.visitJumpInsnWithProbe(opcode, label, 119 idGenerator.nextId(), frame(jumpPopCount(opcode))); 120 } else { 121 probesVisitor.visitJumpInsn(opcode, label); 122 } 123 } 124 jumpPopCount(final int opcode)125 private int jumpPopCount(final int opcode) { 126 switch (opcode) { 127 case Opcodes.GOTO: 128 return 0; 129 case Opcodes.IFEQ: 130 case Opcodes.IFNE: 131 case Opcodes.IFLT: 132 case Opcodes.IFGE: 133 case Opcodes.IFGT: 134 case Opcodes.IFLE: 135 case Opcodes.IFNULL: 136 case Opcodes.IFNONNULL: 137 return 1; 138 default: // IF_CMPxx and IF_ACMPxx 139 return 2; 140 } 141 } 142 143 @Override visitLookupSwitchInsn(final Label dflt, final int[] keys, final Label[] labels)144 public void visitLookupSwitchInsn(final Label dflt, final int[] keys, 145 final Label[] labels) { 146 if (markLabels(dflt, labels)) { 147 probesVisitor.visitLookupSwitchInsnWithProbes(dflt, keys, labels, 148 frame(1)); 149 } else { 150 probesVisitor.visitLookupSwitchInsn(dflt, keys, labels); 151 } 152 } 153 154 @Override visitTableSwitchInsn(final int min, final int max, final Label dflt, final Label... labels)155 public void visitTableSwitchInsn(final int min, final int max, 156 final Label dflt, final Label... labels) { 157 if (markLabels(dflt, labels)) { 158 probesVisitor.visitTableSwitchInsnWithProbes(min, max, dflt, labels, 159 frame(1)); 160 } else { 161 probesVisitor.visitTableSwitchInsn(min, max, dflt, labels); 162 } 163 } 164 markLabels(final Label dflt, final Label[] labels)165 private boolean markLabels(final Label dflt, final Label[] labels) { 166 boolean probe = false; 167 LabelInfo.resetDone(labels); 168 if (LabelInfo.isMultiTarget(dflt)) { 169 LabelInfo.setProbeId(dflt, idGenerator.nextId()); 170 probe = true; 171 } 172 LabelInfo.setDone(dflt); 173 for (final Label l : labels) { 174 if (LabelInfo.isMultiTarget(l) && !LabelInfo.isDone(l)) { 175 LabelInfo.setProbeId(l, idGenerator.nextId()); 176 probe = true; 177 } 178 LabelInfo.setDone(l); 179 } 180 return probe; 181 } 182 frame(final int popCount)183 private IFrame frame(final int popCount) { 184 return FrameSnapshot.create(analyzer, popCount); 185 } 186 187 } 188