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.instr; 14 15 import org.jacoco.core.internal.flow.IFrame; 16 import org.jacoco.core.internal.flow.LabelInfo; 17 import org.jacoco.core.internal.flow.MethodProbesVisitor; 18 import org.objectweb.asm.Label; 19 import org.objectweb.asm.MethodVisitor; 20 import org.objectweb.asm.Opcodes; 21 22 /** 23 * This method adapter inserts probes as requested by the 24 * {@link MethodProbesVisitor} events. 25 */ 26 class MethodInstrumenter extends MethodProbesVisitor { 27 28 private final IProbeInserter probeInserter; 29 30 /** 31 * Create a new instrumenter instance for the given method. 32 * 33 * @param mv 34 * next method visitor in the chain 35 * @param probeInserter 36 * call-back to insert probes where required 37 */ MethodInstrumenter(final MethodVisitor mv, final IProbeInserter probeInserter)38 public MethodInstrumenter(final MethodVisitor mv, 39 final IProbeInserter probeInserter) { 40 super(mv); 41 this.probeInserter = probeInserter; 42 } 43 44 // === IMethodProbesVisitor === 45 46 @Override visitProbe(final int probeId)47 public void visitProbe(final int probeId) { 48 probeInserter.insertProbe(probeId); 49 } 50 51 @Override visitInsnWithProbe(final int opcode, final int probeId)52 public void visitInsnWithProbe(final int opcode, final int probeId) { 53 probeInserter.insertProbe(probeId); 54 mv.visitInsn(opcode); 55 } 56 57 @Override visitJumpInsnWithProbe(final int opcode, final Label label, final int probeId, final IFrame frame)58 public void visitJumpInsnWithProbe(final int opcode, final Label label, 59 final int probeId, final IFrame frame) { 60 if (opcode == Opcodes.GOTO) { 61 probeInserter.insertProbe(probeId); 62 mv.visitJumpInsn(Opcodes.GOTO, label); 63 } else { 64 final Label intermediate = new Label(); 65 mv.visitJumpInsn(getInverted(opcode), intermediate); 66 probeInserter.insertProbe(probeId); 67 mv.visitJumpInsn(Opcodes.GOTO, label); 68 mv.visitLabel(intermediate); 69 frame.accept(mv); 70 } 71 } 72 getInverted(final int opcode)73 private int getInverted(final int opcode) { 74 switch (opcode) { 75 case Opcodes.IFEQ: 76 return Opcodes.IFNE; 77 case Opcodes.IFNE: 78 return Opcodes.IFEQ; 79 case Opcodes.IFLT: 80 return Opcodes.IFGE; 81 case Opcodes.IFGE: 82 return Opcodes.IFLT; 83 case Opcodes.IFGT: 84 return Opcodes.IFLE; 85 case Opcodes.IFLE: 86 return Opcodes.IFGT; 87 case Opcodes.IF_ICMPEQ: 88 return Opcodes.IF_ICMPNE; 89 case Opcodes.IF_ICMPNE: 90 return Opcodes.IF_ICMPEQ; 91 case Opcodes.IF_ICMPLT: 92 return Opcodes.IF_ICMPGE; 93 case Opcodes.IF_ICMPGE: 94 return Opcodes.IF_ICMPLT; 95 case Opcodes.IF_ICMPGT: 96 return Opcodes.IF_ICMPLE; 97 case Opcodes.IF_ICMPLE: 98 return Opcodes.IF_ICMPGT; 99 case Opcodes.IF_ACMPEQ: 100 return Opcodes.IF_ACMPNE; 101 case Opcodes.IF_ACMPNE: 102 return Opcodes.IF_ACMPEQ; 103 case Opcodes.IFNULL: 104 return Opcodes.IFNONNULL; 105 case Opcodes.IFNONNULL: 106 return Opcodes.IFNULL; 107 } 108 throw new IllegalArgumentException(); 109 } 110 111 @Override visitTableSwitchInsnWithProbes(final int min, final int max, final Label dflt, final Label[] labels, final IFrame frame)112 public void visitTableSwitchInsnWithProbes(final int min, final int max, 113 final Label dflt, final Label[] labels, final IFrame frame) { 114 // 1. Calculate intermediate labels: 115 LabelInfo.resetDone(dflt); 116 LabelInfo.resetDone(labels); 117 final Label newDflt = createIntermediate(dflt); 118 final Label[] newLabels = createIntermediates(labels); 119 mv.visitTableSwitchInsn(min, max, newDflt, newLabels); 120 121 // 2. Insert probes: 122 insertIntermediateProbes(dflt, labels, frame); 123 } 124 125 @Override visitLookupSwitchInsnWithProbes(final Label dflt, final int[] keys, final Label[] labels, final IFrame frame)126 public void visitLookupSwitchInsnWithProbes(final Label dflt, 127 final int[] keys, final Label[] labels, final IFrame frame) { 128 // 1. Calculate intermediate labels: 129 LabelInfo.resetDone(dflt); 130 LabelInfo.resetDone(labels); 131 final Label newDflt = createIntermediate(dflt); 132 final Label[] newLabels = createIntermediates(labels); 133 mv.visitLookupSwitchInsn(newDflt, keys, newLabels); 134 135 // 2. Insert probes: 136 insertIntermediateProbes(dflt, labels, frame); 137 } 138 createIntermediates(final Label[] labels)139 private Label[] createIntermediates(final Label[] labels) { 140 final Label[] intermediates = new Label[labels.length]; 141 for (int i = 0; i < labels.length; i++) { 142 intermediates[i] = createIntermediate(labels[i]); 143 } 144 return intermediates; 145 } 146 createIntermediate(final Label label)147 private Label createIntermediate(final Label label) { 148 final Label intermediate; 149 if (LabelInfo.getProbeId(label) == LabelInfo.NO_PROBE) { 150 intermediate = label; 151 } else { 152 if (LabelInfo.isDone(label)) { 153 intermediate = LabelInfo.getIntermediateLabel(label); 154 } else { 155 intermediate = new Label(); 156 LabelInfo.setIntermediateLabel(label, intermediate); 157 LabelInfo.setDone(label); 158 } 159 } 160 return intermediate; 161 } 162 insertIntermediateProbe(final Label label, final IFrame frame)163 private void insertIntermediateProbe(final Label label, 164 final IFrame frame) { 165 final int probeId = LabelInfo.getProbeId(label); 166 if (probeId != LabelInfo.NO_PROBE && !LabelInfo.isDone(label)) { 167 mv.visitLabel(LabelInfo.getIntermediateLabel(label)); 168 frame.accept(mv); 169 probeInserter.insertProbe(probeId); 170 mv.visitJumpInsn(Opcodes.GOTO, label); 171 LabelInfo.setDone(label); 172 } 173 } 174 insertIntermediateProbes(final Label dflt, final Label[] labels, final IFrame frame)175 private void insertIntermediateProbes(final Label dflt, 176 final Label[] labels, final IFrame frame) { 177 LabelInfo.resetDone(dflt); 178 LabelInfo.resetDone(labels); 179 insertIntermediateProbe(dflt, frame); 180 for (final Label l : labels) { 181 insertIntermediateProbe(l, frame); 182 } 183 } 184 185 } 186