1/******************************************************************************* 2 * Copyright (c) 2009, 2015 Mountainminds GmbH & Co. KG and Contributors 3 * All rights reserved. This program and the accompanying materials 4 * are made available under the terms of the Eclipse Public License v1.0 5 * which accompanies this distribution, and is available at 6 * http://www.eclipse.org/legal/epl-v10.html 7 * 8 * Contributors: 9 * Marc R. Hoffmann - initial API and implementation 10 * 11 *******************************************************************************/ 12package org.jacoco.core.internal.flow; 13 14import org.jacoco.core.JaCoCo; 15import org.objectweb.asm.Handle; 16import org.objectweb.asm.Label; 17import org.objectweb.asm.MethodVisitor; 18import org.objectweb.asm.Opcodes; 19import org.objectweb.asm.tree.MethodNode; 20 21/** 22 * Method visitor to collect flow related information about the {@link Label}s 23 * within a class. It calculates the properties "multitarget" and "successor" 24 * that can afterwards be obtained via {@link LabelInfo}. 25 */ 26public final class LabelFlowAnalyzer extends MethodVisitor { 27 28 /** 29 * Marks all labels of the method with control flow information. 30 * 31 * @param method 32 * Method to mark labels 33 */ 34 public static void markLabels(final MethodNode method) { 35 // We do not use the accept() method as ASM resets labels after every 36 // call to accept() 37 final MethodVisitor lfa = new LabelFlowAnalyzer(); 38 for (int i = method.tryCatchBlocks.size(); --i >= 0;) { 39 method.tryCatchBlocks.get(i).accept(lfa); 40 } 41 method.instructions.accept(lfa); 42 } 43 44 /** 45 * <code>true</code> if the current instruction is a potential successor of 46 * the previous instruction. Accessible for testing. 47 */ 48 boolean successor = false; 49 50 /** 51 * <code>true</code> for the very first instruction only. Accessible for 52 * testing. 53 */ 54 boolean first = true; 55 56 /** 57 * Label instance of the last line start. 58 */ 59 Label lineStart = null; 60 61 /** 62 * Create new instance. 63 */ 64 public LabelFlowAnalyzer() { 65 super(JaCoCo.ASM_API_VERSION); 66 } 67 68 @Override 69 public void visitTryCatchBlock(final Label start, final Label end, 70 final Label handler, final String type) { 71 // Enforce probe at the beginning of the block. Assuming the start of 72 // the block already is successor of some other code, adding a target 73 // makes the start a multitarget. However, if the start of the block 74 // also is the start of the method, no probe will be added. 75 LabelInfo.setTarget(start); 76 77 // Mark exception handler as possible target of the block 78 LabelInfo.setTarget(handler); 79 } 80 81 @Override 82 public void visitJumpInsn(final int opcode, final Label label) { 83 LabelInfo.setTarget(label); 84 if (opcode == Opcodes.JSR) { 85 throw new AssertionError("Subroutines not supported."); 86 } 87 successor = opcode != Opcodes.GOTO; 88 first = false; 89 } 90 91 @Override 92 public void visitLabel(final Label label) { 93 if (first) { 94 LabelInfo.setTarget(label); 95 } 96 if (successor) { 97 LabelInfo.setSuccessor(label); 98 } 99 } 100 101 @Override 102 public void visitLineNumber(final int line, final Label start) { 103 lineStart = start; 104 } 105 106 @Override 107 public void visitTableSwitchInsn(final int min, final int max, 108 final Label dflt, final Label... labels) { 109 visitSwitchInsn(dflt, labels); 110 } 111 112 @Override 113 public void visitLookupSwitchInsn(final Label dflt, final int[] keys, 114 final Label[] labels) { 115 visitSwitchInsn(dflt, labels); 116 } 117 118 private void visitSwitchInsn(final Label dflt, final Label[] labels) { 119 LabelInfo.resetDone(dflt); 120 LabelInfo.resetDone(labels); 121 setTargetIfNotDone(dflt); 122 for (final Label l : labels) { 123 setTargetIfNotDone(l); 124 } 125 successor = false; 126 first = false; 127 } 128 129 private static void setTargetIfNotDone(final Label label) { 130 if (!LabelInfo.isDone(label)) { 131 LabelInfo.setTarget(label); 132 LabelInfo.setDone(label); 133 } 134 } 135 136 @Override 137 public void visitInsn(final int opcode) { 138 switch (opcode) { 139 case Opcodes.RET: 140 throw new AssertionError("Subroutines not supported."); 141 case Opcodes.IRETURN: 142 case Opcodes.LRETURN: 143 case Opcodes.FRETURN: 144 case Opcodes.DRETURN: 145 case Opcodes.ARETURN: 146 case Opcodes.RETURN: 147 case Opcodes.ATHROW: 148 successor = false; 149 break; 150 default: 151 successor = true; 152 break; 153 } 154 first = false; 155 } 156 157 @Override 158 public void visitIntInsn(final int opcode, final int operand) { 159 successor = true; 160 first = false; 161 } 162 163 @Override 164 public void visitVarInsn(final int opcode, final int var) { 165 successor = true; 166 first = false; 167 } 168 169 @Override 170 public void visitTypeInsn(final int opcode, final String type) { 171 successor = true; 172 first = false; 173 } 174 175 @Override 176 public void visitFieldInsn(final int opcode, final String owner, 177 final String name, final String desc) { 178 successor = true; 179 first = false; 180 } 181 182 @Override 183 public void visitMethodInsn(final int opcode, final String owner, 184 final String name, final String desc, final boolean itf) { 185 successor = true; 186 first = false; 187 markMethodInvocationLine(); 188 } 189 190 @Override 191 public void visitInvokeDynamicInsn(final String name, final String desc, 192 final Handle bsm, final Object... bsmArgs) { 193 successor = true; 194 first = false; 195 markMethodInvocationLine(); 196 } 197 198 private void markMethodInvocationLine() { 199 if (lineStart != null) { 200 LabelInfo.setMethodInvocationLine(lineStart); 201 } 202 } 203 204 @Override 205 public void visitLdcInsn(final Object cst) { 206 successor = true; 207 first = false; 208 } 209 210 @Override 211 public void visitIincInsn(final int var, final int increment) { 212 successor = true; 213 first = false; 214 } 215 216 @Override 217 public void visitMultiANewArrayInsn(final String desc, final int dims) { 218 successor = true; 219 first = false; 220 } 221 222} 223